@rely-ai/caliber 1.30.7 → 1.31.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 +704 -406
- package/package.json +21 -2
package/dist/bin.js
CHANGED
|
@@ -25,7 +25,7 @@ __export(config_exports, {
|
|
|
25
25
|
writeConfigFile: () => writeConfigFile
|
|
26
26
|
});
|
|
27
27
|
import fs4 from "fs";
|
|
28
|
-
import
|
|
28
|
+
import path6 from "path";
|
|
29
29
|
import os2 from "os";
|
|
30
30
|
function getMaxPromptTokens() {
|
|
31
31
|
const config = loadConfig();
|
|
@@ -125,8 +125,8 @@ var CONFIG_DIR, CONFIG_FILE, DEFAULT_MODELS, MODEL_CONTEXT_WINDOWS, DEFAULT_CONT
|
|
|
125
125
|
var init_config = __esm({
|
|
126
126
|
"src/llm/config.ts"() {
|
|
127
127
|
"use strict";
|
|
128
|
-
CONFIG_DIR =
|
|
129
|
-
CONFIG_FILE =
|
|
128
|
+
CONFIG_DIR = path6.join(os2.homedir(), ".caliber");
|
|
129
|
+
CONFIG_FILE = path6.join(CONFIG_DIR, "config.json");
|
|
130
130
|
DEFAULT_MODELS = {
|
|
131
131
|
anthropic: "claude-sonnet-4-6",
|
|
132
132
|
vertex: "claude-sonnet-4-6",
|
|
@@ -233,11 +233,11 @@ var init_types = __esm({
|
|
|
233
233
|
// src/utils/editor.ts
|
|
234
234
|
import { execSync as execSync13, spawn as spawn3 } from "child_process";
|
|
235
235
|
import fs26 from "fs";
|
|
236
|
-
import
|
|
236
|
+
import path23 from "path";
|
|
237
237
|
import os6 from "os";
|
|
238
238
|
function getEmptyFilePath(proposedPath) {
|
|
239
239
|
fs26.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
|
|
240
|
-
const tempPath =
|
|
240
|
+
const tempPath = path23.join(DIFF_TEMP_DIR, path23.basename(proposedPath));
|
|
241
241
|
fs26.writeFileSync(tempPath, "");
|
|
242
242
|
return tempPath;
|
|
243
243
|
}
|
|
@@ -278,7 +278,7 @@ var init_editor = __esm({
|
|
|
278
278
|
"src/utils/editor.ts"() {
|
|
279
279
|
"use strict";
|
|
280
280
|
IS_WINDOWS3 = process.platform === "win32";
|
|
281
|
-
DIFF_TEMP_DIR =
|
|
281
|
+
DIFF_TEMP_DIR = path23.join(os6.tmpdir(), "caliber-diff");
|
|
282
282
|
}
|
|
283
283
|
});
|
|
284
284
|
|
|
@@ -504,7 +504,7 @@ __export(lock_exports, {
|
|
|
504
504
|
releaseLock: () => releaseLock
|
|
505
505
|
});
|
|
506
506
|
import fs37 from "fs";
|
|
507
|
-
import
|
|
507
|
+
import path30 from "path";
|
|
508
508
|
import os8 from "os";
|
|
509
509
|
function isCaliberRunning() {
|
|
510
510
|
try {
|
|
@@ -538,7 +538,7 @@ var LOCK_FILE, STALE_MS;
|
|
|
538
538
|
var init_lock = __esm({
|
|
539
539
|
"src/lib/lock.ts"() {
|
|
540
540
|
"use strict";
|
|
541
|
-
LOCK_FILE =
|
|
541
|
+
LOCK_FILE = path30.join(os8.tmpdir(), ".caliber.lock");
|
|
542
542
|
STALE_MS = 10 * 60 * 1e3;
|
|
543
543
|
}
|
|
544
544
|
});
|
|
@@ -546,17 +546,17 @@ var init_lock = __esm({
|
|
|
546
546
|
// src/cli.ts
|
|
547
547
|
import { Command } from "commander";
|
|
548
548
|
import fs47 from "fs";
|
|
549
|
-
import
|
|
549
|
+
import path39 from "path";
|
|
550
550
|
import { fileURLToPath } from "url";
|
|
551
551
|
|
|
552
552
|
// src/commands/init.ts
|
|
553
|
-
import
|
|
553
|
+
import path26 from "path";
|
|
554
554
|
import chalk14 from "chalk";
|
|
555
555
|
import fs32 from "fs";
|
|
556
556
|
|
|
557
557
|
// src/fingerprint/index.ts
|
|
558
558
|
import fs8 from "fs";
|
|
559
|
-
import
|
|
559
|
+
import path8 from "path";
|
|
560
560
|
|
|
561
561
|
// src/fingerprint/git.ts
|
|
562
562
|
import { execSync } from "child_process";
|
|
@@ -801,10 +801,25 @@ function readExistingConfigs(dir) {
|
|
|
801
801
|
|
|
802
802
|
// src/fingerprint/code-analysis.ts
|
|
803
803
|
import fs3 from "fs";
|
|
804
|
-
import
|
|
804
|
+
import path5 from "path";
|
|
805
805
|
import { execSync as execSync3 } from "child_process";
|
|
806
806
|
|
|
807
807
|
// src/lib/sanitize.ts
|
|
808
|
+
import path4 from "path";
|
|
809
|
+
function sanitizePath(component) {
|
|
810
|
+
const cleaned = component.replace(/\.\./g, "").replace(/[/\\]/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
|
|
811
|
+
if (!cleaned) {
|
|
812
|
+
throw new Error(`Invalid path component: ${component}`);
|
|
813
|
+
}
|
|
814
|
+
return cleaned;
|
|
815
|
+
}
|
|
816
|
+
function assertPathWithinDir(filePath, baseDir) {
|
|
817
|
+
const resolved = path4.resolve(baseDir, filePath);
|
|
818
|
+
const resolvedBase = path4.resolve(baseDir);
|
|
819
|
+
if (!resolved.startsWith(resolvedBase + path4.sep) && resolved !== resolvedBase) {
|
|
820
|
+
throw new Error(`Path traversal detected: ${filePath} escapes ${baseDir}`);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
808
823
|
var KNOWN_PREFIX_PATTERNS = [
|
|
809
824
|
// Anthropic (before generic sk- pattern)
|
|
810
825
|
[/sk-ant-[A-Za-z0-9_-]{20,}/g, "[REDACTED]"],
|
|
@@ -828,9 +843,13 @@ var KNOWN_PREFIX_PATTERNS = [
|
|
|
828
843
|
// Bearer tokens
|
|
829
844
|
[/[Bb]earer\s+[A-Za-z0-9_\-.]{20,}/g, "[REDACTED]"],
|
|
830
845
|
// PEM private keys
|
|
831
|
-
[/-----BEGIN[A-Z ]+KEY-----[\s\S]+?-----END[A-Z ]+KEY-----/g, "[REDACTED]"]
|
|
846
|
+
[/-----BEGIN[A-Z ]+KEY-----[\s\S]+?-----END[A-Z ]+KEY-----/g, "[REDACTED]"],
|
|
847
|
+
// Database connection strings with credentials
|
|
848
|
+
[/(postgresql|mysql|mongodb(\+srv)?|redis|amqp):\/\/[^@\s]+:[^@\s]+@[^\s'"]+/g, "[REDACTED]"],
|
|
849
|
+
// GitLab tokens
|
|
850
|
+
[/glpat-[a-zA-Z0-9_-]{20,}/g, "[REDACTED]"]
|
|
832
851
|
];
|
|
833
|
-
var SENSITIVE_ASSIGNMENT = /(?:api[_-]?key|secret[_-]?key|password|token|credential|auth[_-]?token|private[_-]?key)\s*[:=]\s*['"]?([^\s'"]{8,500})['"]?/gi;
|
|
852
|
+
var SENSITIVE_ASSIGNMENT = /(?:api[_-]?key|secret[_-]?key|password|token|credential|auth[_-]?token|private[_-]?key|database[_-]?url|connection[_-]?string)\s*[:=]\s*['"]?([^\s'"]{8,500})['"]?/gi;
|
|
834
853
|
function sanitizeSecrets(text) {
|
|
835
854
|
let result = text;
|
|
836
855
|
for (const [pattern, replacement] of KNOWN_PREFIX_PATTERNS) {
|
|
@@ -949,12 +968,15 @@ var SKIP_PATTERNS = [
|
|
|
949
968
|
/\.d\.ts$/,
|
|
950
969
|
/\.generated\./,
|
|
951
970
|
/\.snap$/,
|
|
952
|
-
/^\.env($|\.)
|
|
971
|
+
/^\.env($|\.)/,
|
|
972
|
+
/\.(pem|key|crt|cer|pfx|p12|jks|keystore)$/,
|
|
973
|
+
/^id_(rsa|ed25519|ecdsa)$/,
|
|
974
|
+
/^(known_hosts|authorized_keys)$/
|
|
953
975
|
];
|
|
954
976
|
var COMMENT_LINE = {
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
977
|
+
c: /^\s*\/\//,
|
|
978
|
+
h: /^\s*#/,
|
|
979
|
+
x: /^\s*<!--.*-->\s*$/
|
|
958
980
|
};
|
|
959
981
|
var EXT_COMMENT = {
|
|
960
982
|
".ts": "c",
|
|
@@ -1060,7 +1082,6 @@ function extractSkeleton(content, ext) {
|
|
|
1060
1082
|
const lines = content.split("\n");
|
|
1061
1083
|
const result = [];
|
|
1062
1084
|
let braceDepth = 0;
|
|
1063
|
-
let inSignature = false;
|
|
1064
1085
|
let skipBody = false;
|
|
1065
1086
|
if ([".py", ".pyw", ".rb"].includes(ext)) {
|
|
1066
1087
|
return extractSkeletonIndentBased(lines, ext);
|
|
@@ -1086,7 +1107,9 @@ function extractSkeleton(content, ext) {
|
|
|
1086
1107
|
}
|
|
1087
1108
|
continue;
|
|
1088
1109
|
}
|
|
1089
|
-
const isFnOrClass = /^\s*(export\s+)?(default\s+)?(async\s+)?(function|class|const\s+\w+\s*=\s*(async\s*)?\(|pub\s+fn|fn|func)\s/.test(
|
|
1110
|
+
const isFnOrClass = /^\s*(export\s+)?(default\s+)?(async\s+)?(function|class|const\s+\w+\s*=\s*(async\s*)?\(|pub\s+fn|fn|func)\s/.test(
|
|
1111
|
+
trimmed
|
|
1112
|
+
) || /^\s*(def|func|fn|pub fn|pub async fn)\s/.test(trimmed);
|
|
1090
1113
|
if (isFnOrClass && braceDepth === 0) {
|
|
1091
1114
|
result.push(line);
|
|
1092
1115
|
const opens = (line.match(/{/g) || []).length;
|
|
@@ -1114,7 +1137,7 @@ function extractSkeleton(content, ext) {
|
|
|
1114
1137
|
}
|
|
1115
1138
|
return result.join("\n");
|
|
1116
1139
|
}
|
|
1117
|
-
function extractSkeletonIndentBased(lines,
|
|
1140
|
+
function extractSkeletonIndentBased(lines, _ext) {
|
|
1118
1141
|
const result = [];
|
|
1119
1142
|
let skipIndent = -1;
|
|
1120
1143
|
for (const line of lines) {
|
|
@@ -1146,18 +1169,18 @@ function extractSkeletonIndentBased(lines, ext) {
|
|
|
1146
1169
|
}
|
|
1147
1170
|
function extractImports(content, filePath) {
|
|
1148
1171
|
const imports = [];
|
|
1149
|
-
const dir =
|
|
1172
|
+
const dir = path5.dirname(filePath);
|
|
1150
1173
|
for (const line of content.split("\n")) {
|
|
1151
1174
|
const trimmed = line.trim();
|
|
1152
1175
|
const jsMatch = trimmed.match(/(?:from|require\()\s*['"]([^'"]+)['"]/);
|
|
1153
1176
|
if (jsMatch && jsMatch[1].startsWith(".")) {
|
|
1154
|
-
imports.push(
|
|
1177
|
+
imports.push(path5.normalize(path5.join(dir, jsMatch[1])));
|
|
1155
1178
|
continue;
|
|
1156
1179
|
}
|
|
1157
1180
|
const pyMatch = trimmed.match(/^from\s+(\.[.\w]*)\s+import/);
|
|
1158
1181
|
if (pyMatch) {
|
|
1159
1182
|
const modulePath = pyMatch[1].replace(/\./g, "/");
|
|
1160
|
-
imports.push(
|
|
1183
|
+
imports.push(path5.normalize(path5.join(dir, modulePath)));
|
|
1161
1184
|
continue;
|
|
1162
1185
|
}
|
|
1163
1186
|
const goMatch = trimmed.match(/^\s*"([^"]+)"/);
|
|
@@ -1170,11 +1193,20 @@ function extractImports(content, filePath) {
|
|
|
1170
1193
|
function buildImportCounts(files) {
|
|
1171
1194
|
const counts = /* @__PURE__ */ new Map();
|
|
1172
1195
|
for (const [filePath, content] of files) {
|
|
1173
|
-
const ext =
|
|
1196
|
+
const ext = path5.extname(filePath).toLowerCase();
|
|
1174
1197
|
if (!SOURCE_EXTENSIONS.has(ext)) continue;
|
|
1175
1198
|
const imports = extractImports(content, filePath);
|
|
1176
1199
|
for (const imp of imports) {
|
|
1177
|
-
const candidates = [
|
|
1200
|
+
const candidates = [
|
|
1201
|
+
imp,
|
|
1202
|
+
imp + ".ts",
|
|
1203
|
+
imp + ".js",
|
|
1204
|
+
imp + ".tsx",
|
|
1205
|
+
imp + ".jsx",
|
|
1206
|
+
imp + "/index.ts",
|
|
1207
|
+
imp + "/index.js",
|
|
1208
|
+
imp + ".py"
|
|
1209
|
+
];
|
|
1178
1210
|
for (const candidate of candidates) {
|
|
1179
1211
|
const normalized = candidate.replace(/\\/g, "/");
|
|
1180
1212
|
if (files.has(normalized)) {
|
|
@@ -1204,7 +1236,7 @@ function getGitFrequency(dir) {
|
|
|
1204
1236
|
function groupByDirectory(files) {
|
|
1205
1237
|
const groups = /* @__PURE__ */ new Map();
|
|
1206
1238
|
for (const f of files) {
|
|
1207
|
-
const dir =
|
|
1239
|
+
const dir = path5.dirname(f.path);
|
|
1208
1240
|
const group = groups.get(dir) || [];
|
|
1209
1241
|
group.push(f);
|
|
1210
1242
|
groups.set(dir, group);
|
|
@@ -1216,7 +1248,9 @@ function structuralFingerprint(content, ext) {
|
|
|
1216
1248
|
const bucket = Math.floor(lines.length / 10) * 10;
|
|
1217
1249
|
const first = (lines[0] || "").trim().slice(0, 50);
|
|
1218
1250
|
const imports = lines.filter((l) => /^\s*(import |from |require\(|use )/.test(l)).length;
|
|
1219
|
-
const fns = lines.filter(
|
|
1251
|
+
const fns = lines.filter(
|
|
1252
|
+
(l) => /^\s*(export\s+)?(async\s+)?(function |def |func |fn |pub fn |class )/.test(l)
|
|
1253
|
+
).length;
|
|
1220
1254
|
return `${ext}:${bucket}:${imports}:${fns}:${first}`;
|
|
1221
1255
|
}
|
|
1222
1256
|
function analyzeCode(dir) {
|
|
@@ -1225,14 +1259,14 @@ function analyzeCode(dir) {
|
|
|
1225
1259
|
let totalChars = 0;
|
|
1226
1260
|
for (const relPath of allPaths) {
|
|
1227
1261
|
try {
|
|
1228
|
-
totalChars += fs3.statSync(
|
|
1262
|
+
totalChars += fs3.statSync(path5.join(dir, relPath)).size;
|
|
1229
1263
|
} catch {
|
|
1230
1264
|
}
|
|
1231
1265
|
}
|
|
1232
1266
|
const fileContents = /* @__PURE__ */ new Map();
|
|
1233
1267
|
for (const relPath of allPaths) {
|
|
1234
1268
|
try {
|
|
1235
|
-
const content = fs3.readFileSync(
|
|
1269
|
+
const content = fs3.readFileSync(path5.join(dir, relPath), "utf-8");
|
|
1236
1270
|
if (content.split("\n").length <= 500) fileContents.set(relPath, content);
|
|
1237
1271
|
} catch {
|
|
1238
1272
|
}
|
|
@@ -1242,7 +1276,7 @@ function analyzeCode(dir) {
|
|
|
1242
1276
|
const scored = [];
|
|
1243
1277
|
let compressedChars = 0;
|
|
1244
1278
|
for (const [relPath, rawContent] of fileContents) {
|
|
1245
|
-
const ext =
|
|
1279
|
+
const ext = path5.extname(relPath).toLowerCase();
|
|
1246
1280
|
const compressed = compressContent(rawContent, ext);
|
|
1247
1281
|
const skeleton = extractSkeleton(compressed, ext);
|
|
1248
1282
|
compressedChars += compressed.length;
|
|
@@ -1263,7 +1297,12 @@ function analyzeCode(dir) {
|
|
|
1263
1297
|
const repFP = structuralFingerprint(rep.compressed, rep.ext);
|
|
1264
1298
|
const similar = group.slice(1).filter((f) => structuralFingerprint(f.compressed, f.ext) === repFP);
|
|
1265
1299
|
const unique = group.slice(1).filter((f) => structuralFingerprint(f.compressed, f.ext) !== repFP);
|
|
1266
|
-
const repEntry = {
|
|
1300
|
+
const repEntry = {
|
|
1301
|
+
path: rep.path,
|
|
1302
|
+
content: sanitizeSecrets(rep.compressed),
|
|
1303
|
+
size: rep.compressed.length,
|
|
1304
|
+
priority: rep.score
|
|
1305
|
+
};
|
|
1267
1306
|
const repSize = rep.path.length + rep.compressed.length + 10;
|
|
1268
1307
|
if (includedChars + repSize <= CHAR_BUDGET) {
|
|
1269
1308
|
result.push(repEntry);
|
|
@@ -1271,18 +1310,28 @@ function analyzeCode(dir) {
|
|
|
1271
1310
|
}
|
|
1272
1311
|
if (similar.length > 0) {
|
|
1273
1312
|
dupGroups++;
|
|
1274
|
-
const names = similar.map((f) =>
|
|
1313
|
+
const names = similar.map((f) => path5.basename(f.path));
|
|
1275
1314
|
const summary = `(${similar.length} similar file${similar.length === 1 ? "" : "s"} in ${dirPath}/: ${names.join(", ")})`;
|
|
1276
1315
|
const summarySize = summary.length + 30;
|
|
1277
1316
|
if (includedChars + summarySize <= CHAR_BUDGET) {
|
|
1278
|
-
result.push({
|
|
1317
|
+
result.push({
|
|
1318
|
+
path: `[similar to ${rep.path}]`,
|
|
1319
|
+
content: summary,
|
|
1320
|
+
size: summary.length,
|
|
1321
|
+
priority: rep.score
|
|
1322
|
+
});
|
|
1279
1323
|
includedChars += summarySize;
|
|
1280
1324
|
}
|
|
1281
1325
|
}
|
|
1282
1326
|
for (const f of unique) {
|
|
1283
1327
|
const skeletonSize = f.path.length + f.skeleton.length + 10;
|
|
1284
1328
|
if (includedChars + skeletonSize <= CHAR_BUDGET) {
|
|
1285
|
-
result.push({
|
|
1329
|
+
result.push({
|
|
1330
|
+
path: f.path,
|
|
1331
|
+
content: sanitizeSecrets(f.skeleton),
|
|
1332
|
+
size: f.skeleton.length,
|
|
1333
|
+
priority: f.score
|
|
1334
|
+
});
|
|
1286
1335
|
includedChars += skeletonSize;
|
|
1287
1336
|
}
|
|
1288
1337
|
}
|
|
@@ -1292,7 +1341,12 @@ function analyzeCode(dir) {
|
|
|
1292
1341
|
if (includedPaths.has(f.path)) continue;
|
|
1293
1342
|
const skeletonSize = f.path.length + f.skeleton.length + 10;
|
|
1294
1343
|
if (includedChars + skeletonSize > CHAR_BUDGET) continue;
|
|
1295
|
-
result.push({
|
|
1344
|
+
result.push({
|
|
1345
|
+
path: f.path,
|
|
1346
|
+
content: sanitizeSecrets(f.skeleton),
|
|
1347
|
+
size: f.skeleton.length,
|
|
1348
|
+
priority: f.score
|
|
1349
|
+
});
|
|
1296
1350
|
includedChars += skeletonSize;
|
|
1297
1351
|
}
|
|
1298
1352
|
return {
|
|
@@ -1308,7 +1362,7 @@ function analyzeCode(dir) {
|
|
|
1308
1362
|
}
|
|
1309
1363
|
function walkDir(base, rel, depth, maxDepth, files) {
|
|
1310
1364
|
if (depth > maxDepth) return;
|
|
1311
|
-
const fullPath =
|
|
1365
|
+
const fullPath = path5.join(base, rel);
|
|
1312
1366
|
let entries;
|
|
1313
1367
|
try {
|
|
1314
1368
|
entries = fs3.readdirSync(fullPath, { withFileTypes: true });
|
|
@@ -1324,7 +1378,7 @@ function walkDir(base, rel, depth, maxDepth, files) {
|
|
|
1324
1378
|
} else if (entry.isFile()) {
|
|
1325
1379
|
if (SKIP_FILES.has(entry.name)) continue;
|
|
1326
1380
|
if (SKIP_PATTERNS.some((p) => p.test(entry.name))) continue;
|
|
1327
|
-
const ext =
|
|
1381
|
+
const ext = path5.extname(entry.name).toLowerCase();
|
|
1328
1382
|
if (TEXT_EXTENSIONS.has(ext) || depth === 0 && !ext && !entry.name.startsWith(".")) {
|
|
1329
1383
|
files.push(relPath);
|
|
1330
1384
|
}
|
|
@@ -1332,7 +1386,7 @@ function walkDir(base, rel, depth, maxDepth, files) {
|
|
|
1332
1386
|
}
|
|
1333
1387
|
}
|
|
1334
1388
|
function filePriority(filePath) {
|
|
1335
|
-
const base =
|
|
1389
|
+
const base = path5.basename(filePath);
|
|
1336
1390
|
const entryPoints = /* @__PURE__ */ new Set([
|
|
1337
1391
|
"index.ts",
|
|
1338
1392
|
"index.js",
|
|
@@ -2076,7 +2130,7 @@ function isClaudeCliAvailable() {
|
|
|
2076
2130
|
|
|
2077
2131
|
// src/llm/utils.ts
|
|
2078
2132
|
function extractJson(text) {
|
|
2079
|
-
const startIdx = text.search(/[
|
|
2133
|
+
const startIdx = text.search(/[[{]/);
|
|
2080
2134
|
if (startIdx === -1) return null;
|
|
2081
2135
|
let depth = 0;
|
|
2082
2136
|
let inString = false;
|
|
@@ -2831,14 +2885,14 @@ init_config();
|
|
|
2831
2885
|
|
|
2832
2886
|
// src/fingerprint/cache.ts
|
|
2833
2887
|
import fs7 from "fs";
|
|
2834
|
-
import
|
|
2888
|
+
import path7 from "path";
|
|
2835
2889
|
import crypto from "crypto";
|
|
2836
2890
|
import { execSync as execSync7 } from "child_process";
|
|
2837
2891
|
var CACHE_VERSION = 1;
|
|
2838
2892
|
var CACHE_DIR = ".caliber/cache";
|
|
2839
2893
|
var CACHE_FILE = "fingerprint.json";
|
|
2840
2894
|
function getCachePath(dir) {
|
|
2841
|
-
return
|
|
2895
|
+
return path7.join(dir, CACHE_DIR, CACHE_FILE);
|
|
2842
2896
|
}
|
|
2843
2897
|
function getGitHead(dir) {
|
|
2844
2898
|
try {
|
|
@@ -2897,7 +2951,7 @@ function loadFingerprintCache(dir, fileTree) {
|
|
|
2897
2951
|
function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks, tools, workspaces) {
|
|
2898
2952
|
const cachePath = getCachePath(dir);
|
|
2899
2953
|
try {
|
|
2900
|
-
const cacheDir =
|
|
2954
|
+
const cacheDir = path7.dirname(cachePath);
|
|
2901
2955
|
if (!fs7.existsSync(cacheDir)) {
|
|
2902
2956
|
fs7.mkdirSync(cacheDir, { recursive: true });
|
|
2903
2957
|
}
|
|
@@ -2971,7 +3025,7 @@ async function collectFingerprint(dir) {
|
|
|
2971
3025
|
}
|
|
2972
3026
|
function readPackageName(dir) {
|
|
2973
3027
|
try {
|
|
2974
|
-
const pkgPath =
|
|
3028
|
+
const pkgPath = path8.join(dir, "package.json");
|
|
2975
3029
|
if (!fs8.existsSync(pkgPath)) return void 0;
|
|
2976
3030
|
const pkg3 = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
|
|
2977
3031
|
return pkg3.name;
|
|
@@ -2987,7 +3041,7 @@ async function enrichWithLLM(fingerprint) {
|
|
|
2987
3041
|
const suffixCounts = {};
|
|
2988
3042
|
for (const entry of fingerprint.fileTree) {
|
|
2989
3043
|
if (entry.endsWith("/")) continue;
|
|
2990
|
-
const ext =
|
|
3044
|
+
const ext = path8.extname(entry).toLowerCase();
|
|
2991
3045
|
if (ext) {
|
|
2992
3046
|
suffixCounts[ext] = (suffixCounts[ext] || 0) + 1;
|
|
2993
3047
|
}
|
|
@@ -3004,20 +3058,20 @@ async function enrichWithLLM(fingerprint) {
|
|
|
3004
3058
|
|
|
3005
3059
|
// src/scanner/index.ts
|
|
3006
3060
|
import fs9 from "fs";
|
|
3007
|
-
import
|
|
3061
|
+
import path9 from "path";
|
|
3008
3062
|
import crypto2 from "crypto";
|
|
3009
3063
|
import os4 from "os";
|
|
3010
3064
|
function detectPlatforms() {
|
|
3011
3065
|
const home = os4.homedir();
|
|
3012
3066
|
return {
|
|
3013
|
-
claude: fs9.existsSync(
|
|
3067
|
+
claude: fs9.existsSync(path9.join(home, ".claude")),
|
|
3014
3068
|
cursor: fs9.existsSync(getCursorConfigDir()),
|
|
3015
|
-
codex: fs9.existsSync(
|
|
3069
|
+
codex: fs9.existsSync(path9.join(home, ".codex"))
|
|
3016
3070
|
};
|
|
3017
3071
|
}
|
|
3018
3072
|
function scanLocalState(dir) {
|
|
3019
3073
|
const items = [];
|
|
3020
|
-
const claudeMdPath =
|
|
3074
|
+
const claudeMdPath = path9.join(dir, "CLAUDE.md");
|
|
3021
3075
|
if (fs9.existsSync(claudeMdPath)) {
|
|
3022
3076
|
items.push({
|
|
3023
3077
|
type: "rule",
|
|
@@ -3027,10 +3081,10 @@ function scanLocalState(dir) {
|
|
|
3027
3081
|
path: claudeMdPath
|
|
3028
3082
|
});
|
|
3029
3083
|
}
|
|
3030
|
-
const skillsDir =
|
|
3084
|
+
const skillsDir = path9.join(dir, ".claude", "skills");
|
|
3031
3085
|
if (fs9.existsSync(skillsDir)) {
|
|
3032
3086
|
for (const file of fs9.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
3033
|
-
const filePath =
|
|
3087
|
+
const filePath = path9.join(skillsDir, file);
|
|
3034
3088
|
items.push({
|
|
3035
3089
|
type: "skill",
|
|
3036
3090
|
platform: "claude",
|
|
@@ -3040,7 +3094,7 @@ function scanLocalState(dir) {
|
|
|
3040
3094
|
});
|
|
3041
3095
|
}
|
|
3042
3096
|
}
|
|
3043
|
-
const mcpJsonPath =
|
|
3097
|
+
const mcpJsonPath = path9.join(dir, ".mcp.json");
|
|
3044
3098
|
if (fs9.existsSync(mcpJsonPath)) {
|
|
3045
3099
|
try {
|
|
3046
3100
|
const mcpJson = JSON.parse(fs9.readFileSync(mcpJsonPath, "utf-8"));
|
|
@@ -3059,7 +3113,7 @@ function scanLocalState(dir) {
|
|
|
3059
3113
|
warnScanSkip(".mcp.json", error);
|
|
3060
3114
|
}
|
|
3061
3115
|
}
|
|
3062
|
-
const agentsMdPath =
|
|
3116
|
+
const agentsMdPath = path9.join(dir, "AGENTS.md");
|
|
3063
3117
|
if (fs9.existsSync(agentsMdPath)) {
|
|
3064
3118
|
items.push({
|
|
3065
3119
|
type: "rule",
|
|
@@ -3069,11 +3123,11 @@ function scanLocalState(dir) {
|
|
|
3069
3123
|
path: agentsMdPath
|
|
3070
3124
|
});
|
|
3071
3125
|
}
|
|
3072
|
-
const codexSkillsDir =
|
|
3126
|
+
const codexSkillsDir = path9.join(dir, ".agents", "skills");
|
|
3073
3127
|
if (fs9.existsSync(codexSkillsDir)) {
|
|
3074
3128
|
try {
|
|
3075
3129
|
for (const name of fs9.readdirSync(codexSkillsDir)) {
|
|
3076
|
-
const skillFile =
|
|
3130
|
+
const skillFile = path9.join(codexSkillsDir, name, "SKILL.md");
|
|
3077
3131
|
if (fs9.existsSync(skillFile)) {
|
|
3078
3132
|
items.push({
|
|
3079
3133
|
type: "skill",
|
|
@@ -3088,7 +3142,7 @@ function scanLocalState(dir) {
|
|
|
3088
3142
|
warnScanSkip(".agents/skills", error);
|
|
3089
3143
|
}
|
|
3090
3144
|
}
|
|
3091
|
-
const cursorrulesPath =
|
|
3145
|
+
const cursorrulesPath = path9.join(dir, ".cursorrules");
|
|
3092
3146
|
if (fs9.existsSync(cursorrulesPath)) {
|
|
3093
3147
|
items.push({
|
|
3094
3148
|
type: "rule",
|
|
@@ -3098,10 +3152,10 @@ function scanLocalState(dir) {
|
|
|
3098
3152
|
path: cursorrulesPath
|
|
3099
3153
|
});
|
|
3100
3154
|
}
|
|
3101
|
-
const cursorRulesDir =
|
|
3155
|
+
const cursorRulesDir = path9.join(dir, ".cursor", "rules");
|
|
3102
3156
|
if (fs9.existsSync(cursorRulesDir)) {
|
|
3103
3157
|
for (const file of fs9.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
3104
|
-
const filePath =
|
|
3158
|
+
const filePath = path9.join(cursorRulesDir, file);
|
|
3105
3159
|
items.push({
|
|
3106
3160
|
type: "rule",
|
|
3107
3161
|
platform: "cursor",
|
|
@@ -3111,11 +3165,11 @@ function scanLocalState(dir) {
|
|
|
3111
3165
|
});
|
|
3112
3166
|
}
|
|
3113
3167
|
}
|
|
3114
|
-
const cursorSkillsDir =
|
|
3168
|
+
const cursorSkillsDir = path9.join(dir, ".cursor", "skills");
|
|
3115
3169
|
if (fs9.existsSync(cursorSkillsDir)) {
|
|
3116
3170
|
try {
|
|
3117
3171
|
for (const name of fs9.readdirSync(cursorSkillsDir)) {
|
|
3118
|
-
const skillFile =
|
|
3172
|
+
const skillFile = path9.join(cursorSkillsDir, name, "SKILL.md");
|
|
3119
3173
|
if (fs9.existsSync(skillFile)) {
|
|
3120
3174
|
items.push({
|
|
3121
3175
|
type: "skill",
|
|
@@ -3130,7 +3184,7 @@ function scanLocalState(dir) {
|
|
|
3130
3184
|
warnScanSkip(".cursor/skills", error);
|
|
3131
3185
|
}
|
|
3132
3186
|
}
|
|
3133
|
-
const cursorMcpPath =
|
|
3187
|
+
const cursorMcpPath = path9.join(dir, ".cursor", "mcp.json");
|
|
3134
3188
|
if (fs9.existsSync(cursorMcpPath)) {
|
|
3135
3189
|
try {
|
|
3136
3190
|
const mcpJson = JSON.parse(fs9.readFileSync(cursorMcpPath, "utf-8"));
|
|
@@ -3165,17 +3219,17 @@ function warnScanSkip(target, error) {
|
|
|
3165
3219
|
function getCursorConfigDir() {
|
|
3166
3220
|
const home = os4.homedir();
|
|
3167
3221
|
if (process.platform === "darwin") {
|
|
3168
|
-
return
|
|
3222
|
+
return path9.join(home, "Library", "Application Support", "Cursor");
|
|
3169
3223
|
}
|
|
3170
3224
|
if (process.platform === "win32") {
|
|
3171
|
-
return
|
|
3225
|
+
return path9.join(home, "AppData", "Roaming", "Cursor");
|
|
3172
3226
|
}
|
|
3173
|
-
return
|
|
3227
|
+
return path9.join(home, ".config", "Cursor");
|
|
3174
3228
|
}
|
|
3175
3229
|
|
|
3176
3230
|
// src/fingerprint/sources.ts
|
|
3177
3231
|
import fs10 from "fs";
|
|
3178
|
-
import
|
|
3232
|
+
import path10 from "path";
|
|
3179
3233
|
|
|
3180
3234
|
// src/scoring/utils.ts
|
|
3181
3235
|
import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
|
|
@@ -3242,12 +3296,14 @@ function isGitRepo2(dir) {
|
|
|
3242
3296
|
function checkGitIgnored(dir, paths) {
|
|
3243
3297
|
if (paths.length === 0) return /* @__PURE__ */ new Set();
|
|
3244
3298
|
try {
|
|
3245
|
-
const result = execFileSync(
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3299
|
+
const result = execFileSync("git", ["check-ignore", ...paths], {
|
|
3300
|
+
cwd: dir,
|
|
3301
|
+
encoding: "utf-8",
|
|
3302
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3303
|
+
});
|
|
3304
|
+
return new Set(
|
|
3305
|
+
result.split("\n").map((l) => l.trim()).filter(Boolean)
|
|
3249
3306
|
);
|
|
3250
|
-
return new Set(result.split("\n").map((l) => l.trim()).filter(Boolean));
|
|
3251
3307
|
} catch (err) {
|
|
3252
3308
|
if (err && typeof err === "object" && "status" in err && err.status === 1) {
|
|
3253
3309
|
return /* @__PURE__ */ new Set();
|
|
@@ -3271,7 +3327,10 @@ function collectProjectStructure(dir, maxDepth = 2) {
|
|
|
3271
3327
|
if (name.startsWith(".") && IGNORED_DIRS.has(name)) continue;
|
|
3272
3328
|
dirEntries.push({ name, rel: relative(dir, join(currentDir, name)) });
|
|
3273
3329
|
}
|
|
3274
|
-
const gitIgnored = useGit ? checkGitIgnored(
|
|
3330
|
+
const gitIgnored = useGit ? checkGitIgnored(
|
|
3331
|
+
dir,
|
|
3332
|
+
dirEntries.map((d) => d.rel)
|
|
3333
|
+
) : null;
|
|
3275
3334
|
for (const entry of entries) {
|
|
3276
3335
|
const name = entry.name;
|
|
3277
3336
|
if (name.startsWith(".") && IGNORED_DIRS.has(name)) continue;
|
|
@@ -3454,7 +3513,9 @@ function countTreeLines(content) {
|
|
|
3454
3513
|
return count;
|
|
3455
3514
|
}
|
|
3456
3515
|
function calculateDuplicatePercent(content1, content2) {
|
|
3457
|
-
const lines1 = new Set(
|
|
3516
|
+
const lines1 = new Set(
|
|
3517
|
+
content1.split("\n").map((l) => l.trim()).filter((l) => l.length > 10)
|
|
3518
|
+
);
|
|
3458
3519
|
const lines2 = content2.split("\n").map((l) => l.trim()).filter((l) => l.length > 10);
|
|
3459
3520
|
const overlapping = lines2.filter((l) => lines1.has(l)).length;
|
|
3460
3521
|
return lines2.length > 0 ? Math.round(overlapping / lines2.length * 100) : 0;
|
|
@@ -3473,7 +3534,9 @@ function isEntryMentioned(entry, contentLower) {
|
|
|
3473
3534
|
if (lastSegment && lastSegment.length > 3) variants.push(lastSegment);
|
|
3474
3535
|
return variants.some((v) => {
|
|
3475
3536
|
const escaped = v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3476
|
-
return new RegExp(`(?:^|[\\s\`/"'\\.,(])${escaped}(?:[\\s\`/"'.,;:!?)\\\\]|$)`, "i").test(
|
|
3537
|
+
return new RegExp(`(?:^|[\\s\`/"'\\.,(])${escaped}(?:[\\s\`/"'.,;:!?)\\\\]|$)`, "i").test(
|
|
3538
|
+
contentLower
|
|
3539
|
+
);
|
|
3477
3540
|
});
|
|
3478
3541
|
}
|
|
3479
3542
|
function classifyLine(line, inCodeBlock) {
|
|
@@ -3484,7 +3547,8 @@ function classifyLine(line, inCodeBlock) {
|
|
|
3484
3547
|
if (/`[^`]+`/.test(trimmed)) return "concrete";
|
|
3485
3548
|
if (/[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+\.[a-zA-Z]{1,5}/.test(trimmed)) return "concrete";
|
|
3486
3549
|
if (/[a-zA-Z0-9_]{4,}\/[a-zA-Z0-9_.-]/.test(trimmed)) return "concrete";
|
|
3487
|
-
if (/\b[a-zA-Z0-9_-]+\.[a-zA-Z]{1,5}\b/.test(trimmed) && !/\b(e\.g|i\.e|vs|etc)\b/i.test(trimmed))
|
|
3550
|
+
if (/\b[a-zA-Z0-9_-]+\.[a-zA-Z]{1,5}\b/.test(trimmed) && !/\b(e\.g|i\.e|vs|etc)\b/i.test(trimmed))
|
|
3551
|
+
return "concrete";
|
|
3488
3552
|
return "abstract";
|
|
3489
3553
|
}
|
|
3490
3554
|
|
|
@@ -3494,29 +3558,29 @@ var SOURCE_CONTENT_LIMIT = 2e3;
|
|
|
3494
3558
|
var README_CONTENT_LIMIT = 1e3;
|
|
3495
3559
|
var ORIGIN_PRIORITY = { cli: 0, config: 1, workspace: 2 };
|
|
3496
3560
|
function loadSourcesConfig(dir) {
|
|
3497
|
-
const configPath =
|
|
3561
|
+
const configPath = path10.join(dir, ".caliber", "sources.json");
|
|
3498
3562
|
const content = readFileOrNull(configPath);
|
|
3499
3563
|
if (!content) return [];
|
|
3500
3564
|
try {
|
|
3501
3565
|
const parsed = JSON.parse(content);
|
|
3502
3566
|
if (!Array.isArray(parsed.sources)) {
|
|
3503
|
-
console.warn(
|
|
3567
|
+
console.warn(
|
|
3568
|
+
"Warning: .caliber/sources.json is malformed (missing sources array), skipping sources"
|
|
3569
|
+
);
|
|
3504
3570
|
return [];
|
|
3505
3571
|
}
|
|
3506
|
-
return parsed.sources.filter(
|
|
3507
|
-
(s) => s.type && (s.path || s.url)
|
|
3508
|
-
);
|
|
3572
|
+
return parsed.sources.filter((s) => s.type && (s.path || s.url));
|
|
3509
3573
|
} catch {
|
|
3510
3574
|
console.warn("Warning: .caliber/sources.json is malformed, skipping sources");
|
|
3511
3575
|
return [];
|
|
3512
3576
|
}
|
|
3513
3577
|
}
|
|
3514
3578
|
function writeSourcesConfig(dir, sources2) {
|
|
3515
|
-
const configDir =
|
|
3579
|
+
const configDir = path10.join(dir, ".caliber");
|
|
3516
3580
|
if (!fs10.existsSync(configDir)) {
|
|
3517
3581
|
fs10.mkdirSync(configDir, { recursive: true });
|
|
3518
3582
|
}
|
|
3519
|
-
const configPath =
|
|
3583
|
+
const configPath = path10.join(configDir, "sources.json");
|
|
3520
3584
|
fs10.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
|
|
3521
3585
|
}
|
|
3522
3586
|
function detectSourceType(absPath) {
|
|
@@ -3527,14 +3591,14 @@ function detectSourceType(absPath) {
|
|
|
3527
3591
|
}
|
|
3528
3592
|
}
|
|
3529
3593
|
function isInsideDir(childPath, parentDir) {
|
|
3530
|
-
const relative2 =
|
|
3531
|
-
return !relative2.startsWith("..") && !
|
|
3594
|
+
const relative2 = path10.relative(parentDir, childPath);
|
|
3595
|
+
return !relative2.startsWith("..") && !path10.isAbsolute(relative2);
|
|
3532
3596
|
}
|
|
3533
3597
|
function resolveAllSources(dir, cliSources, workspaces) {
|
|
3534
3598
|
const seen = /* @__PURE__ */ new Map();
|
|
3535
|
-
const projectRoot =
|
|
3599
|
+
const projectRoot = path10.resolve(dir);
|
|
3536
3600
|
for (const src of cliSources) {
|
|
3537
|
-
const absPath =
|
|
3601
|
+
const absPath = path10.resolve(dir, src);
|
|
3538
3602
|
if (seen.has(absPath)) continue;
|
|
3539
3603
|
const type = detectSourceType(absPath);
|
|
3540
3604
|
seen.set(absPath, {
|
|
@@ -3547,12 +3611,12 @@ function resolveAllSources(dir, cliSources, workspaces) {
|
|
|
3547
3611
|
for (const cfg of configSources) {
|
|
3548
3612
|
if (cfg.type === "url") continue;
|
|
3549
3613
|
if (!cfg.path) continue;
|
|
3550
|
-
const absPath =
|
|
3614
|
+
const absPath = path10.resolve(dir, cfg.path);
|
|
3551
3615
|
if (seen.has(absPath)) continue;
|
|
3552
3616
|
seen.set(absPath, { absPath, config: cfg, origin: "config" });
|
|
3553
3617
|
}
|
|
3554
3618
|
for (const ws of workspaces) {
|
|
3555
|
-
const absPath =
|
|
3619
|
+
const absPath = path10.resolve(dir, ws);
|
|
3556
3620
|
if (seen.has(absPath)) continue;
|
|
3557
3621
|
if (!isInsideDir(absPath, projectRoot)) continue;
|
|
3558
3622
|
seen.set(absPath, {
|
|
@@ -3594,13 +3658,13 @@ function collectSourceSummary(resolved, projectDir) {
|
|
|
3594
3658
|
if (config.type === "file") {
|
|
3595
3659
|
return collectFileSummary(resolved, projectDir);
|
|
3596
3660
|
}
|
|
3597
|
-
const summaryPath =
|
|
3661
|
+
const summaryPath = path10.join(absPath, ".caliber", "summary.json");
|
|
3598
3662
|
const summaryContent = readFileOrNull(summaryPath);
|
|
3599
3663
|
if (summaryContent) {
|
|
3600
3664
|
try {
|
|
3601
3665
|
const published = JSON.parse(summaryContent);
|
|
3602
3666
|
return {
|
|
3603
|
-
name: published.name ||
|
|
3667
|
+
name: published.name || path10.basename(absPath),
|
|
3604
3668
|
type: "repo",
|
|
3605
3669
|
role: config.role || published.role || "related-repo",
|
|
3606
3670
|
description: config.description || published.description || "",
|
|
@@ -3614,7 +3678,7 @@ function collectSourceSummary(resolved, projectDir) {
|
|
|
3614
3678
|
}
|
|
3615
3679
|
return collectRepoSummary(resolved, projectDir);
|
|
3616
3680
|
}
|
|
3617
|
-
function collectRepoSummary(resolved,
|
|
3681
|
+
function collectRepoSummary(resolved, _projectDir) {
|
|
3618
3682
|
const { config, origin, absPath } = resolved;
|
|
3619
3683
|
const packageName = readPackageName(absPath);
|
|
3620
3684
|
let topLevelDirs;
|
|
@@ -3625,13 +3689,13 @@ function collectRepoSummary(resolved, projectDir) {
|
|
|
3625
3689
|
keyFiles = entries.filter((e) => e.isFile() && !e.name.startsWith(".")).map((e) => e.name).slice(0, 15);
|
|
3626
3690
|
} catch {
|
|
3627
3691
|
}
|
|
3628
|
-
const claudeMdContent = readFileOrNull(
|
|
3692
|
+
const claudeMdContent = readFileOrNull(path10.join(absPath, "CLAUDE.md"));
|
|
3629
3693
|
const existingClaudeMd = claudeMdContent ? claudeMdContent.slice(0, SOURCE_CONTENT_LIMIT) : void 0;
|
|
3630
|
-
const readmeContent = readFileOrNull(
|
|
3694
|
+
const readmeContent = readFileOrNull(path10.join(absPath, "README.md"));
|
|
3631
3695
|
const readmeExcerpt = readmeContent ? readmeContent.slice(0, README_CONTENT_LIMIT) : void 0;
|
|
3632
3696
|
const gitRemoteUrl = getGitRemoteUrl(absPath);
|
|
3633
3697
|
return {
|
|
3634
|
-
name: packageName ||
|
|
3698
|
+
name: packageName || path10.basename(absPath),
|
|
3635
3699
|
type: "repo",
|
|
3636
3700
|
role: config.role || "related-repo",
|
|
3637
3701
|
description: config.description || "",
|
|
@@ -3644,11 +3708,11 @@ function collectRepoSummary(resolved, projectDir) {
|
|
|
3644
3708
|
packageName
|
|
3645
3709
|
};
|
|
3646
3710
|
}
|
|
3647
|
-
function collectFileSummary(resolved,
|
|
3711
|
+
function collectFileSummary(resolved, _projectDir) {
|
|
3648
3712
|
const { config, origin, absPath } = resolved;
|
|
3649
3713
|
const content = readFileOrNull(absPath);
|
|
3650
3714
|
return {
|
|
3651
|
-
name:
|
|
3715
|
+
name: path10.basename(absPath),
|
|
3652
3716
|
type: "file",
|
|
3653
3717
|
role: config.role || "reference-doc",
|
|
3654
3718
|
description: config.description || content?.slice(0, 100).split("\n")[0] || "",
|
|
@@ -3692,15 +3756,15 @@ init_config();
|
|
|
3692
3756
|
// src/utils/dependencies.ts
|
|
3693
3757
|
import { readFileSync as readFileSync2 } from "fs";
|
|
3694
3758
|
import { join as join2 } from "path";
|
|
3695
|
-
function readFileOrNull2(
|
|
3759
|
+
function readFileOrNull2(path41) {
|
|
3696
3760
|
try {
|
|
3697
|
-
return readFileSync2(
|
|
3761
|
+
return readFileSync2(path41, "utf-8");
|
|
3698
3762
|
} catch {
|
|
3699
3763
|
return null;
|
|
3700
3764
|
}
|
|
3701
3765
|
}
|
|
3702
|
-
function readJsonOrNull(
|
|
3703
|
-
const content = readFileOrNull2(
|
|
3766
|
+
function readJsonOrNull(path41) {
|
|
3767
|
+
const content = readFileOrNull2(path41);
|
|
3704
3768
|
if (!content) return null;
|
|
3705
3769
|
try {
|
|
3706
3770
|
return JSON.parse(content);
|
|
@@ -3742,18 +3806,24 @@ function extractNpmDeps(dir) {
|
|
|
3742
3806
|
/^prettier-/,
|
|
3743
3807
|
/^@typescript-eslint\//
|
|
3744
3808
|
];
|
|
3745
|
-
return Object.keys(deps).filter(
|
|
3809
|
+
return Object.keys(deps).filter(
|
|
3810
|
+
(d) => !trivial.has(d) && !d.startsWith("@types/") && !trivialPatterns.some((p) => p.test(d))
|
|
3811
|
+
).slice(0, 30);
|
|
3746
3812
|
}
|
|
3747
3813
|
function extractPythonDeps(dir) {
|
|
3748
3814
|
const reqTxt = readFileOrNull2(join2(dir, "requirements.txt"));
|
|
3749
3815
|
if (reqTxt) {
|
|
3750
|
-
return reqTxt.split("\n").map(
|
|
3816
|
+
return reqTxt.split("\n").map(
|
|
3817
|
+
(l) => l.trim().split(/[=<>!~[]/)[0].trim()
|
|
3818
|
+
).filter((l) => l && !l.startsWith("#")).slice(0, 30);
|
|
3751
3819
|
}
|
|
3752
3820
|
const pyproject = readFileOrNull2(join2(dir, "pyproject.toml"));
|
|
3753
3821
|
if (pyproject) {
|
|
3754
3822
|
const depMatch = pyproject.match(/dependencies\s*=\s*\[([\s\S]*?)\]/);
|
|
3755
3823
|
if (depMatch) {
|
|
3756
|
-
return depMatch[1].split("\n").map(
|
|
3824
|
+
return depMatch[1].split("\n").map(
|
|
3825
|
+
(l) => l.trim().replace(/["',]/g, "").split(/[=<>!~[]/)[0].trim()
|
|
3826
|
+
).filter((l) => l.length > 0).slice(0, 30);
|
|
3757
3827
|
}
|
|
3758
3828
|
}
|
|
3759
3829
|
return [];
|
|
@@ -4291,7 +4361,7 @@ import fs18 from "fs";
|
|
|
4291
4361
|
|
|
4292
4362
|
// src/writers/claude/index.ts
|
|
4293
4363
|
import fs11 from "fs";
|
|
4294
|
-
import
|
|
4364
|
+
import path11 from "path";
|
|
4295
4365
|
|
|
4296
4366
|
// src/writers/pre-commit-block.ts
|
|
4297
4367
|
init_resolve_caliber();
|
|
@@ -4368,9 +4438,9 @@ function writeClaudeConfig(config) {
|
|
|
4368
4438
|
written.push("CLAUDE.md");
|
|
4369
4439
|
if (config.skills?.length) {
|
|
4370
4440
|
for (const skill of config.skills) {
|
|
4371
|
-
const skillDir =
|
|
4441
|
+
const skillDir = path11.join(".claude", "skills", skill.name);
|
|
4372
4442
|
if (!fs11.existsSync(skillDir)) fs11.mkdirSync(skillDir, { recursive: true });
|
|
4373
|
-
const skillPath =
|
|
4443
|
+
const skillPath = path11.join(skillDir, "SKILL.md");
|
|
4374
4444
|
const frontmatter = [
|
|
4375
4445
|
"---",
|
|
4376
4446
|
`name: ${skill.name}`,
|
|
@@ -4400,7 +4470,7 @@ function writeClaudeConfig(config) {
|
|
|
4400
4470
|
|
|
4401
4471
|
// src/writers/cursor/index.ts
|
|
4402
4472
|
import fs12 from "fs";
|
|
4403
|
-
import
|
|
4473
|
+
import path12 from "path";
|
|
4404
4474
|
function writeCursorConfig(config) {
|
|
4405
4475
|
const written = [];
|
|
4406
4476
|
if (config.cursorrules) {
|
|
@@ -4410,18 +4480,18 @@ function writeCursorConfig(config) {
|
|
|
4410
4480
|
const preCommitRule = getCursorPreCommitRule();
|
|
4411
4481
|
const learningsRule = getCursorLearningsRule();
|
|
4412
4482
|
const allRules = [...config.rules || [], preCommitRule, learningsRule];
|
|
4413
|
-
const rulesDir =
|
|
4483
|
+
const rulesDir = path12.join(".cursor", "rules");
|
|
4414
4484
|
if (!fs12.existsSync(rulesDir)) fs12.mkdirSync(rulesDir, { recursive: true });
|
|
4415
4485
|
for (const rule of allRules) {
|
|
4416
|
-
const rulePath =
|
|
4486
|
+
const rulePath = path12.join(rulesDir, rule.filename);
|
|
4417
4487
|
fs12.writeFileSync(rulePath, rule.content);
|
|
4418
4488
|
written.push(rulePath);
|
|
4419
4489
|
}
|
|
4420
4490
|
if (config.skills?.length) {
|
|
4421
4491
|
for (const skill of config.skills) {
|
|
4422
|
-
const skillDir =
|
|
4492
|
+
const skillDir = path12.join(".cursor", "skills", skill.name);
|
|
4423
4493
|
if (!fs12.existsSync(skillDir)) fs12.mkdirSync(skillDir, { recursive: true });
|
|
4424
|
-
const skillPath =
|
|
4494
|
+
const skillPath = path12.join(skillDir, "SKILL.md");
|
|
4425
4495
|
const frontmatter = [
|
|
4426
4496
|
"---",
|
|
4427
4497
|
`name: ${skill.name}`,
|
|
@@ -4436,7 +4506,7 @@ function writeCursorConfig(config) {
|
|
|
4436
4506
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
4437
4507
|
const cursorDir = ".cursor";
|
|
4438
4508
|
if (!fs12.existsSync(cursorDir)) fs12.mkdirSync(cursorDir, { recursive: true });
|
|
4439
|
-
const mcpPath =
|
|
4509
|
+
const mcpPath = path12.join(cursorDir, "mcp.json");
|
|
4440
4510
|
let existingServers = {};
|
|
4441
4511
|
try {
|
|
4442
4512
|
if (fs12.existsSync(mcpPath)) {
|
|
@@ -4454,16 +4524,16 @@ function writeCursorConfig(config) {
|
|
|
4454
4524
|
|
|
4455
4525
|
// src/writers/codex/index.ts
|
|
4456
4526
|
import fs13 from "fs";
|
|
4457
|
-
import
|
|
4527
|
+
import path13 from "path";
|
|
4458
4528
|
function writeCodexConfig(config) {
|
|
4459
4529
|
const written = [];
|
|
4460
4530
|
fs13.writeFileSync("AGENTS.md", appendLearningsBlock(appendPreCommitBlock(config.agentsMd)));
|
|
4461
4531
|
written.push("AGENTS.md");
|
|
4462
4532
|
if (config.skills?.length) {
|
|
4463
4533
|
for (const skill of config.skills) {
|
|
4464
|
-
const skillDir =
|
|
4534
|
+
const skillDir = path13.join(".agents", "skills", skill.name);
|
|
4465
4535
|
if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
|
|
4466
|
-
const skillPath =
|
|
4536
|
+
const skillPath = path13.join(skillDir, "SKILL.md");
|
|
4467
4537
|
const frontmatter = [
|
|
4468
4538
|
"---",
|
|
4469
4539
|
`name: ${skill.name}`,
|
|
@@ -4480,19 +4550,19 @@ function writeCodexConfig(config) {
|
|
|
4480
4550
|
|
|
4481
4551
|
// src/writers/github-copilot/index.ts
|
|
4482
4552
|
import fs14 from "fs";
|
|
4483
|
-
import
|
|
4553
|
+
import path14 from "path";
|
|
4484
4554
|
function writeGithubCopilotConfig(config) {
|
|
4485
4555
|
const written = [];
|
|
4486
4556
|
if (config.instructions) {
|
|
4487
4557
|
fs14.mkdirSync(".github", { recursive: true });
|
|
4488
|
-
fs14.writeFileSync(
|
|
4558
|
+
fs14.writeFileSync(path14.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(config.instructions)));
|
|
4489
4559
|
written.push(".github/copilot-instructions.md");
|
|
4490
4560
|
}
|
|
4491
4561
|
if (config.instructionFiles?.length) {
|
|
4492
|
-
const instructionsDir =
|
|
4562
|
+
const instructionsDir = path14.join(".github", "instructions");
|
|
4493
4563
|
fs14.mkdirSync(instructionsDir, { recursive: true });
|
|
4494
4564
|
for (const file of config.instructionFiles) {
|
|
4495
|
-
fs14.writeFileSync(
|
|
4565
|
+
fs14.writeFileSync(path14.join(instructionsDir, file.filename), file.content);
|
|
4496
4566
|
written.push(`.github/instructions/${file.filename}`);
|
|
4497
4567
|
}
|
|
4498
4568
|
}
|
|
@@ -4501,14 +4571,14 @@ function writeGithubCopilotConfig(config) {
|
|
|
4501
4571
|
|
|
4502
4572
|
// src/writers/backup.ts
|
|
4503
4573
|
import fs15 from "fs";
|
|
4504
|
-
import
|
|
4574
|
+
import path15 from "path";
|
|
4505
4575
|
function createBackup(files) {
|
|
4506
4576
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4507
|
-
const backupDir =
|
|
4577
|
+
const backupDir = path15.join(BACKUPS_DIR, timestamp);
|
|
4508
4578
|
for (const file of files) {
|
|
4509
4579
|
if (!fs15.existsSync(file)) continue;
|
|
4510
|
-
const dest =
|
|
4511
|
-
const destDir =
|
|
4580
|
+
const dest = path15.join(backupDir, file);
|
|
4581
|
+
const destDir = path15.dirname(dest);
|
|
4512
4582
|
if (!fs15.existsSync(destDir)) {
|
|
4513
4583
|
fs15.mkdirSync(destDir, { recursive: true });
|
|
4514
4584
|
}
|
|
@@ -4517,9 +4587,9 @@ function createBackup(files) {
|
|
|
4517
4587
|
return backupDir;
|
|
4518
4588
|
}
|
|
4519
4589
|
function restoreBackup(backupDir, file) {
|
|
4520
|
-
const backupFile =
|
|
4590
|
+
const backupFile = path15.join(backupDir, file);
|
|
4521
4591
|
if (!fs15.existsSync(backupFile)) return false;
|
|
4522
|
-
const destDir =
|
|
4592
|
+
const destDir = path15.dirname(file);
|
|
4523
4593
|
if (!fs15.existsSync(destDir)) {
|
|
4524
4594
|
fs15.mkdirSync(destDir, { recursive: true });
|
|
4525
4595
|
}
|
|
@@ -4530,7 +4600,7 @@ function restoreBackup(backupDir, file) {
|
|
|
4530
4600
|
// src/lib/builtin-skills.ts
|
|
4531
4601
|
init_resolve_caliber();
|
|
4532
4602
|
import fs16 from "fs";
|
|
4533
|
-
import
|
|
4603
|
+
import path16 from "path";
|
|
4534
4604
|
function buildSkillContent(skill) {
|
|
4535
4605
|
const frontmatter = `---
|
|
4536
4606
|
name: ${skill.name}
|
|
@@ -4668,18 +4738,18 @@ var SAVE_LEARNING_SKILL = {
|
|
|
4668
4738
|
};
|
|
4669
4739
|
var BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL];
|
|
4670
4740
|
var PLATFORM_CONFIGS = [
|
|
4671
|
-
{ platformDir: ".claude", skillsDir:
|
|
4672
|
-
{ platformDir: ".cursor", skillsDir:
|
|
4673
|
-
{ platformDir: ".agents", skillsDir:
|
|
4741
|
+
{ platformDir: ".claude", skillsDir: path16.join(".claude", "skills") },
|
|
4742
|
+
{ platformDir: ".cursor", skillsDir: path16.join(".cursor", "skills") },
|
|
4743
|
+
{ platformDir: ".agents", skillsDir: path16.join(".agents", "skills") }
|
|
4674
4744
|
];
|
|
4675
4745
|
function ensureBuiltinSkills() {
|
|
4676
4746
|
const written = [];
|
|
4677
4747
|
for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
|
|
4678
4748
|
if (!fs16.existsSync(platformDir)) continue;
|
|
4679
4749
|
for (const skill of BUILTIN_SKILLS) {
|
|
4680
|
-
const skillPath =
|
|
4750
|
+
const skillPath = path16.join(skillsDir, skill.name, "SKILL.md");
|
|
4681
4751
|
if (fs16.existsSync(skillPath)) continue;
|
|
4682
|
-
fs16.mkdirSync(
|
|
4752
|
+
fs16.mkdirSync(path16.dirname(skillPath), { recursive: true });
|
|
4683
4753
|
fs16.writeFileSync(skillPath, buildSkillContent(skill));
|
|
4684
4754
|
written.push(skillPath);
|
|
4685
4755
|
}
|
|
@@ -4713,10 +4783,7 @@ function fileChecksum(filePath) {
|
|
|
4713
4783
|
function writeSetup(setup) {
|
|
4714
4784
|
const filesToWrite = getFilesToWrite(setup);
|
|
4715
4785
|
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs18.existsSync(f));
|
|
4716
|
-
const existingFiles = [
|
|
4717
|
-
...filesToWrite.filter((f) => fs18.existsSync(f)),
|
|
4718
|
-
...filesToDelete
|
|
4719
|
-
];
|
|
4786
|
+
const existingFiles = [...filesToWrite.filter((f) => fs18.existsSync(f)), ...filesToDelete];
|
|
4720
4787
|
const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
|
|
4721
4788
|
const written = [];
|
|
4722
4789
|
if (setup.targetAgent.includes("claude") && setup.claude) {
|
|
@@ -4809,7 +4876,8 @@ function getFilesToWrite(setup) {
|
|
|
4809
4876
|
if (setup.targetAgent.includes("github-copilot") && setup.copilot) {
|
|
4810
4877
|
if (setup.copilot.instructions) files.push(".github/copilot-instructions.md");
|
|
4811
4878
|
if (setup.copilot.instructionFiles) {
|
|
4812
|
-
for (const f of setup.copilot.instructionFiles)
|
|
4879
|
+
for (const f of setup.copilot.instructionFiles)
|
|
4880
|
+
files.push(`.github/instructions/${f.filename}`);
|
|
4813
4881
|
}
|
|
4814
4882
|
}
|
|
4815
4883
|
return files;
|
|
@@ -4828,10 +4896,10 @@ function ensureGitignore() {
|
|
|
4828
4896
|
|
|
4829
4897
|
// src/writers/staging.ts
|
|
4830
4898
|
import fs19 from "fs";
|
|
4831
|
-
import
|
|
4832
|
-
var STAGED_DIR =
|
|
4833
|
-
var PROPOSED_DIR =
|
|
4834
|
-
var CURRENT_DIR =
|
|
4899
|
+
import path17 from "path";
|
|
4900
|
+
var STAGED_DIR = path17.join(CALIBER_DIR, "staged");
|
|
4901
|
+
var PROPOSED_DIR = path17.join(STAGED_DIR, "proposed");
|
|
4902
|
+
var CURRENT_DIR = path17.join(STAGED_DIR, "current");
|
|
4835
4903
|
function normalizeContent(content) {
|
|
4836
4904
|
return content.split("\n").map((line) => line.trimEnd()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
4837
4905
|
}
|
|
@@ -4841,22 +4909,29 @@ function stageFiles(files, projectDir) {
|
|
|
4841
4909
|
let modifiedFiles = 0;
|
|
4842
4910
|
const stagedFiles = [];
|
|
4843
4911
|
for (const file of files) {
|
|
4844
|
-
|
|
4912
|
+
assertPathWithinDir(file.path, projectDir);
|
|
4913
|
+
const originalPath = path17.join(projectDir, file.path);
|
|
4845
4914
|
if (fs19.existsSync(originalPath)) {
|
|
4846
4915
|
const existing = fs19.readFileSync(originalPath, "utf-8");
|
|
4847
4916
|
if (normalizeContent(existing) === normalizeContent(file.content)) {
|
|
4848
4917
|
continue;
|
|
4849
4918
|
}
|
|
4850
4919
|
}
|
|
4851
|
-
const proposedPath =
|
|
4852
|
-
fs19.mkdirSync(
|
|
4920
|
+
const proposedPath = path17.join(PROPOSED_DIR, file.path);
|
|
4921
|
+
fs19.mkdirSync(path17.dirname(proposedPath), { recursive: true });
|
|
4853
4922
|
fs19.writeFileSync(proposedPath, file.content);
|
|
4854
4923
|
if (fs19.existsSync(originalPath)) {
|
|
4855
|
-
const currentPath =
|
|
4856
|
-
fs19.mkdirSync(
|
|
4924
|
+
const currentPath = path17.join(CURRENT_DIR, file.path);
|
|
4925
|
+
fs19.mkdirSync(path17.dirname(currentPath), { recursive: true });
|
|
4857
4926
|
fs19.copyFileSync(originalPath, currentPath);
|
|
4858
4927
|
modifiedFiles++;
|
|
4859
|
-
stagedFiles.push({
|
|
4928
|
+
stagedFiles.push({
|
|
4929
|
+
relativePath: file.path,
|
|
4930
|
+
proposedPath,
|
|
4931
|
+
currentPath,
|
|
4932
|
+
originalPath,
|
|
4933
|
+
isNew: false
|
|
4934
|
+
});
|
|
4860
4935
|
} else {
|
|
4861
4936
|
newFiles++;
|
|
4862
4937
|
stagedFiles.push({ relativePath: file.path, proposedPath, isNew: true });
|
|
@@ -4882,11 +4957,17 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
4882
4957
|
const skills = claude.skills;
|
|
4883
4958
|
if (Array.isArray(skills)) {
|
|
4884
4959
|
for (const skill of skills) {
|
|
4885
|
-
files.push({
|
|
4960
|
+
files.push({
|
|
4961
|
+
path: `.claude/skills/${sanitizePath(skill.name)}/SKILL.md`,
|
|
4962
|
+
content: buildSkillContent(skill)
|
|
4963
|
+
});
|
|
4886
4964
|
}
|
|
4887
4965
|
}
|
|
4888
4966
|
for (const builtin of BUILTIN_SKILLS) {
|
|
4889
|
-
files.push({
|
|
4967
|
+
files.push({
|
|
4968
|
+
path: `.claude/skills/${builtin.name}/SKILL.md`,
|
|
4969
|
+
content: buildSkillContent(builtin)
|
|
4970
|
+
});
|
|
4890
4971
|
}
|
|
4891
4972
|
}
|
|
4892
4973
|
if (codex) {
|
|
@@ -4894,38 +4975,58 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
4894
4975
|
const codexSkills = codex.skills;
|
|
4895
4976
|
if (Array.isArray(codexSkills)) {
|
|
4896
4977
|
for (const skill of codexSkills) {
|
|
4897
|
-
files.push({
|
|
4978
|
+
files.push({
|
|
4979
|
+
path: `.agents/skills/${sanitizePath(skill.name)}/SKILL.md`,
|
|
4980
|
+
content: buildSkillContent(skill)
|
|
4981
|
+
});
|
|
4898
4982
|
}
|
|
4899
4983
|
}
|
|
4900
4984
|
for (const builtin of BUILTIN_SKILLS) {
|
|
4901
|
-
files.push({
|
|
4985
|
+
files.push({
|
|
4986
|
+
path: `.agents/skills/${builtin.name}/SKILL.md`,
|
|
4987
|
+
content: buildSkillContent(builtin)
|
|
4988
|
+
});
|
|
4902
4989
|
}
|
|
4903
4990
|
}
|
|
4904
4991
|
if (cursor) {
|
|
4905
|
-
if (cursor.cursorrules)
|
|
4992
|
+
if (cursor.cursorrules)
|
|
4993
|
+
files.push({ path: ".cursorrules", content: cursor.cursorrules });
|
|
4906
4994
|
const cursorSkills = cursor.skills;
|
|
4907
4995
|
if (Array.isArray(cursorSkills)) {
|
|
4908
4996
|
for (const skill of cursorSkills) {
|
|
4909
|
-
files.push({
|
|
4997
|
+
files.push({
|
|
4998
|
+
path: `.cursor/skills/${sanitizePath(skill.name)}/SKILL.md`,
|
|
4999
|
+
content: buildSkillContent(skill)
|
|
5000
|
+
});
|
|
4910
5001
|
}
|
|
4911
5002
|
}
|
|
4912
5003
|
for (const builtin of BUILTIN_SKILLS) {
|
|
4913
|
-
files.push({
|
|
5004
|
+
files.push({
|
|
5005
|
+
path: `.cursor/skills/${builtin.name}/SKILL.md`,
|
|
5006
|
+
content: buildSkillContent(builtin)
|
|
5007
|
+
});
|
|
4914
5008
|
}
|
|
4915
5009
|
const rules = cursor.rules;
|
|
4916
5010
|
if (Array.isArray(rules)) {
|
|
4917
5011
|
for (const rule of rules) {
|
|
4918
|
-
files.push({ path: `.cursor/rules/${rule.filename}`, content: rule.content });
|
|
5012
|
+
files.push({ path: `.cursor/rules/${sanitizePath(rule.filename)}`, content: rule.content });
|
|
4919
5013
|
}
|
|
4920
5014
|
}
|
|
4921
5015
|
}
|
|
4922
5016
|
const copilot = setup.copilot;
|
|
4923
5017
|
if (copilot) {
|
|
4924
|
-
if (copilot.instructions)
|
|
5018
|
+
if (copilot.instructions)
|
|
5019
|
+
files.push({
|
|
5020
|
+
path: ".github/copilot-instructions.md",
|
|
5021
|
+
content: copilot.instructions
|
|
5022
|
+
});
|
|
4925
5023
|
const instructionFiles = copilot.instructionFiles;
|
|
4926
5024
|
if (Array.isArray(instructionFiles)) {
|
|
4927
5025
|
for (const file of instructionFiles) {
|
|
4928
|
-
files.push({
|
|
5026
|
+
files.push({
|
|
5027
|
+
path: `.github/instructions/${sanitizePath(file.filename)}`,
|
|
5028
|
+
content: file.content
|
|
5029
|
+
});
|
|
4929
5030
|
}
|
|
4930
5031
|
}
|
|
4931
5032
|
}
|
|
@@ -4934,7 +5035,8 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
4934
5035
|
const agentRefs = [];
|
|
4935
5036
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
4936
5037
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
4937
|
-
if (agentRefs.length === 0)
|
|
5038
|
+
if (agentRefs.length === 0)
|
|
5039
|
+
agentRefs.push("See CLAUDE.md and .cursor/rules/ for agent configurations.");
|
|
4938
5040
|
const stubContent = `# AGENTS.md
|
|
4939
5041
|
|
|
4940
5042
|
This project uses AI coding agents configured by [Caliber](https://github.com/caliber-ai-org/ai-setup).
|
|
@@ -4949,8 +5051,8 @@ ${agentRefs.join(" ")}
|
|
|
4949
5051
|
// src/lib/learning-hooks.ts
|
|
4950
5052
|
init_resolve_caliber();
|
|
4951
5053
|
import fs21 from "fs";
|
|
4952
|
-
import
|
|
4953
|
-
var SETTINGS_PATH =
|
|
5054
|
+
import path18 from "path";
|
|
5055
|
+
var SETTINGS_PATH = path18.join(".claude", "settings.json");
|
|
4954
5056
|
var HOOK_TAILS = [
|
|
4955
5057
|
{ event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
|
|
4956
5058
|
{ event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
|
|
@@ -4975,7 +5077,7 @@ function readSettings() {
|
|
|
4975
5077
|
}
|
|
4976
5078
|
}
|
|
4977
5079
|
function writeSettings(settings) {
|
|
4978
|
-
const dir =
|
|
5080
|
+
const dir = path18.dirname(SETTINGS_PATH);
|
|
4979
5081
|
if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
|
|
4980
5082
|
fs21.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
4981
5083
|
}
|
|
@@ -5011,7 +5113,7 @@ function installLearningHooks() {
|
|
|
5011
5113
|
writeSettings(settings);
|
|
5012
5114
|
return { installed: true, alreadyInstalled: false };
|
|
5013
5115
|
}
|
|
5014
|
-
var CURSOR_HOOKS_PATH =
|
|
5116
|
+
var CURSOR_HOOKS_PATH = path18.join(".cursor", "hooks.json");
|
|
5015
5117
|
var CURSOR_HOOK_EVENTS = [
|
|
5016
5118
|
{ event: "postToolUse", tail: "learn observe" },
|
|
5017
5119
|
{ event: "postToolUseFailure", tail: "learn observe --failure" },
|
|
@@ -5027,7 +5129,7 @@ function readCursorHooks() {
|
|
|
5027
5129
|
}
|
|
5028
5130
|
}
|
|
5029
5131
|
function writeCursorHooks(config) {
|
|
5030
|
-
const dir =
|
|
5132
|
+
const dir = path18.dirname(CURSOR_HOOKS_PATH);
|
|
5031
5133
|
if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
|
|
5032
5134
|
fs21.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
|
|
5033
5135
|
}
|
|
@@ -5102,9 +5204,9 @@ init_resolve_caliber();
|
|
|
5102
5204
|
|
|
5103
5205
|
// src/lib/state.ts
|
|
5104
5206
|
import fs22 from "fs";
|
|
5105
|
-
import
|
|
5207
|
+
import path19 from "path";
|
|
5106
5208
|
import { execSync as execSync8 } from "child_process";
|
|
5107
|
-
var STATE_FILE =
|
|
5209
|
+
var STATE_FILE = path19.join(CALIBER_DIR, ".caliber-state.json");
|
|
5108
5210
|
function normalizeTargetAgent(value) {
|
|
5109
5211
|
if (Array.isArray(value)) return value;
|
|
5110
5212
|
if (typeof value === "string") {
|
|
@@ -5312,8 +5414,8 @@ var SECRET_PATTERNS = [
|
|
|
5312
5414
|
/AKIA[A-Z0-9]{16}/,
|
|
5313
5415
|
/ghp_[a-zA-Z0-9]{36}/,
|
|
5314
5416
|
/ghu_[a-zA-Z0-9]{36}/,
|
|
5315
|
-
/glpat-[a-zA-Z0-
|
|
5316
|
-
/xox[bpors]-[a-zA-Z0-9
|
|
5417
|
+
/glpat-[a-zA-Z0-9_-]{20,}/,
|
|
5418
|
+
/xox[bpors]-[a-zA-Z0-9-]{10,}/,
|
|
5317
5419
|
/(?:password|secret|token|api_key)\s*[:=]\s*["'][^"']{8,}["']/i
|
|
5318
5420
|
];
|
|
5319
5421
|
var SECRET_PLACEHOLDER_PATTERNS = [
|
|
@@ -5325,31 +5427,13 @@ var SECRET_PLACEHOLDER_PATTERNS = [
|
|
|
5325
5427
|
/CHANGE[_-]?ME/i,
|
|
5326
5428
|
/<[^>]+>/
|
|
5327
5429
|
];
|
|
5328
|
-
var CURSOR_ONLY_CHECKS = /* @__PURE__ */ new Set([
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
]);
|
|
5332
|
-
var
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
]);
|
|
5336
|
-
var BOTH_ONLY_CHECKS = /* @__PURE__ */ new Set([
|
|
5337
|
-
"cross_platform_parity",
|
|
5338
|
-
"no_duplicate_content"
|
|
5339
|
-
]);
|
|
5340
|
-
var CODEX_ONLY_CHECKS = /* @__PURE__ */ new Set([
|
|
5341
|
-
"codex_agents_md_exists"
|
|
5342
|
-
]);
|
|
5343
|
-
var COPILOT_ONLY_CHECKS = /* @__PURE__ */ new Set([
|
|
5344
|
-
"copilot_instructions_exists"
|
|
5345
|
-
]);
|
|
5346
|
-
var NON_CODEX_CHECKS = /* @__PURE__ */ new Set([
|
|
5347
|
-
"agents_md_exists"
|
|
5348
|
-
]);
|
|
5349
|
-
var CLAUDE_OR_CODEX_CHECKS = /* @__PURE__ */ new Set([
|
|
5350
|
-
"skills_exist",
|
|
5351
|
-
"open_skills_format"
|
|
5352
|
-
]);
|
|
5430
|
+
var CURSOR_ONLY_CHECKS = /* @__PURE__ */ new Set(["cursor_rules_exist", "cursor_mdc_rules"]);
|
|
5431
|
+
var CLAUDE_ONLY_CHECKS = /* @__PURE__ */ new Set(["claude_md_exists", "claude_md_freshness"]);
|
|
5432
|
+
var BOTH_ONLY_CHECKS = /* @__PURE__ */ new Set(["cross_platform_parity", "no_duplicate_content"]);
|
|
5433
|
+
var CODEX_ONLY_CHECKS = /* @__PURE__ */ new Set(["codex_agents_md_exists"]);
|
|
5434
|
+
var COPILOT_ONLY_CHECKS = /* @__PURE__ */ new Set(["copilot_instructions_exists"]);
|
|
5435
|
+
var NON_CODEX_CHECKS = /* @__PURE__ */ new Set(["agents_md_exists"]);
|
|
5436
|
+
var CLAUDE_OR_CODEX_CHECKS = /* @__PURE__ */ new Set(["skills_exist", "open_skills_format"]);
|
|
5353
5437
|
var GRADE_THRESHOLDS = [
|
|
5354
5438
|
{ minScore: 85, grade: "A" },
|
|
5355
5439
|
{ minScore: 70, grade: "B" },
|
|
@@ -5616,7 +5700,12 @@ function checkQuality(dir) {
|
|
|
5616
5700
|
suggestion: concretenessPoints < 3 && totalMeaningful > 0 ? `${abstractCount} lines are generic prose \u2014 replace with specific instructions referencing project files` : void 0,
|
|
5617
5701
|
fix: concretenessPoints < 3 && totalMeaningful > 0 ? {
|
|
5618
5702
|
action: "replace_vague",
|
|
5619
|
-
data: {
|
|
5703
|
+
data: {
|
|
5704
|
+
abstractLines: abstractExamples,
|
|
5705
|
+
abstractCount,
|
|
5706
|
+
concreteCount,
|
|
5707
|
+
ratio: Math.round(concreteRatio * 100)
|
|
5708
|
+
},
|
|
5620
5709
|
instruction: `Replace generic prose with specific references. Examples of vague lines: ${abstractExamples.join("; ")}`
|
|
5621
5710
|
} : void 0
|
|
5622
5711
|
});
|
|
@@ -5743,7 +5832,7 @@ function checkGrounding(dir) {
|
|
|
5743
5832
|
}
|
|
5744
5833
|
|
|
5745
5834
|
// src/scoring/checks/accuracy.ts
|
|
5746
|
-
import { existsSync as existsSync4, statSync
|
|
5835
|
+
import { existsSync as existsSync4, statSync } from "fs";
|
|
5747
5836
|
import { execSync as execSync9 } from "child_process";
|
|
5748
5837
|
import { join as join5 } from "path";
|
|
5749
5838
|
init_resolve_caliber();
|
|
@@ -5769,7 +5858,7 @@ function detectGitDrift(dir) {
|
|
|
5769
5858
|
const filePath = join5(dir, file);
|
|
5770
5859
|
if (!existsSync4(filePath)) continue;
|
|
5771
5860
|
try {
|
|
5772
|
-
const mtime =
|
|
5861
|
+
const mtime = statSync(filePath).mtime.getTime();
|
|
5773
5862
|
if (mtime > headTime) {
|
|
5774
5863
|
return { commitsSinceConfigUpdate: 0, lastConfigCommit: "uncommitted (recently modified)", isGitRepo: true };
|
|
5775
5864
|
}
|
|
@@ -5882,7 +5971,7 @@ function checkAccuracy(dir) {
|
|
|
5882
5971
|
|
|
5883
5972
|
// src/scoring/checks/freshness.ts
|
|
5884
5973
|
init_resolve_caliber();
|
|
5885
|
-
import { existsSync as existsSync5, statSync as
|
|
5974
|
+
import { existsSync as existsSync5, statSync as statSync2 } from "fs";
|
|
5886
5975
|
import { execSync as execSync10 } from "child_process";
|
|
5887
5976
|
import { join as join6 } from "path";
|
|
5888
5977
|
function getCommitsSinceConfigUpdate(dir) {
|
|
@@ -5897,7 +5986,7 @@ function getCommitsSinceConfigUpdate(dir) {
|
|
|
5897
5986
|
const filePath = join6(dir, file);
|
|
5898
5987
|
if (!existsSync5(filePath)) continue;
|
|
5899
5988
|
try {
|
|
5900
|
-
const mtime =
|
|
5989
|
+
const mtime = statSync2(filePath).mtime.getTime();
|
|
5901
5990
|
if (mtime > headTime) {
|
|
5902
5991
|
return 0;
|
|
5903
5992
|
}
|
|
@@ -6194,8 +6283,8 @@ function checkSources(dir) {
|
|
|
6194
6283
|
|
|
6195
6284
|
// src/scoring/dismissed.ts
|
|
6196
6285
|
import fs23 from "fs";
|
|
6197
|
-
import
|
|
6198
|
-
var DISMISSED_FILE =
|
|
6286
|
+
import path20 from "path";
|
|
6287
|
+
var DISMISSED_FILE = path20.join(CALIBER_DIR, "dismissed-checks.json");
|
|
6199
6288
|
function readDismissedChecks() {
|
|
6200
6289
|
try {
|
|
6201
6290
|
if (!fs23.existsSync(DISMISSED_FILE)) return [];
|
|
@@ -6464,12 +6553,12 @@ import chalk5 from "chalk";
|
|
|
6464
6553
|
|
|
6465
6554
|
// src/telemetry/config.ts
|
|
6466
6555
|
import fs24 from "fs";
|
|
6467
|
-
import
|
|
6556
|
+
import path21 from "path";
|
|
6468
6557
|
import os5 from "os";
|
|
6469
6558
|
import crypto4 from "crypto";
|
|
6470
6559
|
import { execSync as execSync12 } from "child_process";
|
|
6471
|
-
var CONFIG_DIR2 =
|
|
6472
|
-
var CONFIG_FILE2 =
|
|
6560
|
+
var CONFIG_DIR2 = path21.join(os5.homedir(), ".caliber");
|
|
6561
|
+
var CONFIG_FILE2 = path21.join(CONFIG_DIR2, "config.json");
|
|
6473
6562
|
var runtimeDisabled = false;
|
|
6474
6563
|
function readConfig() {
|
|
6475
6564
|
try {
|
|
@@ -6668,7 +6757,7 @@ function detectLocalPlatforms() {
|
|
|
6668
6757
|
return platforms.size > 0 ? Array.from(platforms) : ["claude"];
|
|
6669
6758
|
}
|
|
6670
6759
|
function sanitizeSlug(slug) {
|
|
6671
|
-
return slug.replace(/[^a-zA-Z0-9_
|
|
6760
|
+
return slug.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/^-+|-+$/g, "");
|
|
6672
6761
|
}
|
|
6673
6762
|
function getSkillPath(platform, slug) {
|
|
6674
6763
|
const safe = sanitizeSlug(slug);
|
|
@@ -6706,9 +6795,12 @@ async function searchSkillsSh(technologies) {
|
|
|
6706
6795
|
const bestBySlug = /* @__PURE__ */ new Map();
|
|
6707
6796
|
for (const tech of technologies) {
|
|
6708
6797
|
try {
|
|
6709
|
-
const resp = await fetch(
|
|
6710
|
-
|
|
6711
|
-
|
|
6798
|
+
const resp = await fetch(
|
|
6799
|
+
`https://skills.sh/api/search?q=${encodeURIComponent(tech)}&limit=10`,
|
|
6800
|
+
{
|
|
6801
|
+
signal: AbortSignal.timeout(1e4)
|
|
6802
|
+
}
|
|
6803
|
+
);
|
|
6712
6804
|
if (!resp.ok) continue;
|
|
6713
6805
|
const data = await resp.json();
|
|
6714
6806
|
if (!data.skills?.length) continue;
|
|
@@ -6767,9 +6859,7 @@ async function searchAwesomeClaudeCode(technologies) {
|
|
|
6767
6859
|
}
|
|
6768
6860
|
}
|
|
6769
6861
|
async function searchAllProviders(technologies, platform) {
|
|
6770
|
-
const searches = [
|
|
6771
|
-
searchSkillsSh(technologies)
|
|
6772
|
-
];
|
|
6862
|
+
const searches = [searchSkillsSh(technologies)];
|
|
6773
6863
|
if (platform === "claude" || !platform) {
|
|
6774
6864
|
searches.push(searchAwesomeClaudeCode(technologies));
|
|
6775
6865
|
}
|
|
@@ -6829,18 +6919,24 @@ ${candidateList}`,
|
|
|
6829
6919
|
function buildProjectContext(fingerprint, platforms) {
|
|
6830
6920
|
const parts = [];
|
|
6831
6921
|
if (fingerprint.packageName) parts.push(`Package: ${fingerprint.packageName}`);
|
|
6832
|
-
if (fingerprint.languages.length > 0)
|
|
6833
|
-
|
|
6922
|
+
if (fingerprint.languages.length > 0)
|
|
6923
|
+
parts.push(`Languages: ${fingerprint.languages.join(", ")}`);
|
|
6924
|
+
if (fingerprint.frameworks.length > 0)
|
|
6925
|
+
parts.push(`Frameworks: ${fingerprint.frameworks.join(", ")}`);
|
|
6834
6926
|
if (fingerprint.description) parts.push(`Description: ${fingerprint.description}`);
|
|
6835
6927
|
if (fingerprint.fileTree.length > 0) {
|
|
6836
|
-
parts.push(
|
|
6928
|
+
parts.push(
|
|
6929
|
+
`
|
|
6837
6930
|
File tree (${fingerprint.fileTree.length} files):
|
|
6838
|
-
${fingerprint.fileTree.slice(0, 50).join("\n")}`
|
|
6931
|
+
${fingerprint.fileTree.slice(0, 50).join("\n")}`
|
|
6932
|
+
);
|
|
6839
6933
|
}
|
|
6840
6934
|
if (fingerprint.existingConfigs.claudeMd) {
|
|
6841
|
-
parts.push(
|
|
6935
|
+
parts.push(
|
|
6936
|
+
`
|
|
6842
6937
|
Existing CLAUDE.md (first 500 chars):
|
|
6843
|
-
${fingerprint.existingConfigs.claudeMd.slice(0, 500)}`
|
|
6938
|
+
${fingerprint.existingConfigs.claudeMd.slice(0, 500)}`
|
|
6939
|
+
);
|
|
6844
6940
|
}
|
|
6845
6941
|
const deps = extractTopDeps();
|
|
6846
6942
|
if (deps.length > 0) {
|
|
@@ -6923,20 +7019,18 @@ function extractTopDeps() {
|
|
|
6923
7019
|
/^@typescript-eslint\//,
|
|
6924
7020
|
/^@commitlint\//
|
|
6925
7021
|
];
|
|
6926
|
-
return deps.filter(
|
|
6927
|
-
(d) => !trivial.has(d) && !trivialPatterns.some((p) => p.test(d))
|
|
6928
|
-
);
|
|
7022
|
+
return deps.filter((d) => !trivial.has(d) && !trivialPatterns.some((p) => p.test(d)));
|
|
6929
7023
|
} catch {
|
|
6930
7024
|
return [];
|
|
6931
7025
|
}
|
|
6932
7026
|
}
|
|
6933
7027
|
async function searchSkills(fingerprint, targetPlatforms, onStatus) {
|
|
6934
7028
|
const installedSkills = getInstalledSkills(targetPlatforms);
|
|
6935
|
-
const technologies = [
|
|
6936
|
-
...
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
]
|
|
7029
|
+
const technologies = [
|
|
7030
|
+
...new Set(
|
|
7031
|
+
[...fingerprint.languages, ...fingerprint.frameworks, ...extractTopDeps()].filter(Boolean)
|
|
7032
|
+
)
|
|
7033
|
+
];
|
|
6940
7034
|
if (technologies.length === 0) {
|
|
6941
7035
|
return { results: [], contentMap: /* @__PURE__ */ new Map() };
|
|
6942
7036
|
}
|
|
@@ -6968,10 +7062,12 @@ async function searchSkills(fingerprint, targetPlatforms, onStatus) {
|
|
|
6968
7062
|
}
|
|
6969
7063
|
onStatus?.("Fetching skill content...");
|
|
6970
7064
|
const contentMap = /* @__PURE__ */ new Map();
|
|
6971
|
-
await Promise.all(
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
7065
|
+
await Promise.all(
|
|
7066
|
+
results.map(async (rec) => {
|
|
7067
|
+
const content = await fetchSkillContent(rec);
|
|
7068
|
+
if (content) contentMap.set(rec.slug, content);
|
|
7069
|
+
})
|
|
7070
|
+
);
|
|
6975
7071
|
const available = results.filter((r) => contentMap.has(r.slug));
|
|
6976
7072
|
return { results: available, contentMap };
|
|
6977
7073
|
}
|
|
@@ -7017,10 +7113,12 @@ async function querySkills(query) {
|
|
|
7017
7113
|
const top = results.slice(0, 5);
|
|
7018
7114
|
const fetchSpinner = ora("Verifying availability...").start();
|
|
7019
7115
|
const contentMap = /* @__PURE__ */ new Map();
|
|
7020
|
-
await Promise.all(
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7116
|
+
await Promise.all(
|
|
7117
|
+
top.map(async (rec) => {
|
|
7118
|
+
const content = await fetchSkillContent(rec);
|
|
7119
|
+
if (content) contentMap.set(rec.slug, content);
|
|
7120
|
+
})
|
|
7121
|
+
);
|
|
7024
7122
|
const available = top.filter((r) => contentMap.has(r.slug));
|
|
7025
7123
|
fetchSpinner.succeed(`${available.length} available`);
|
|
7026
7124
|
if (!available.length) {
|
|
@@ -7035,7 +7133,11 @@ async function querySkills(query) {
|
|
|
7035
7133
|
console.log(` ${r.reason || r.name}`);
|
|
7036
7134
|
}
|
|
7037
7135
|
console.log("");
|
|
7038
|
-
console.log(
|
|
7136
|
+
console.log(
|
|
7137
|
+
chalk6.dim(
|
|
7138
|
+
` Install with: ${resolveCaliber()} skills --install ${available.map((r) => r.slug).join(",")}`
|
|
7139
|
+
)
|
|
7140
|
+
);
|
|
7039
7141
|
console.log("");
|
|
7040
7142
|
}
|
|
7041
7143
|
async function installBySlug(slugStr) {
|
|
@@ -7057,10 +7159,12 @@ async function installBySlug(slugStr) {
|
|
|
7057
7159
|
return;
|
|
7058
7160
|
}
|
|
7059
7161
|
const contentMap = /* @__PURE__ */ new Map();
|
|
7060
|
-
await Promise.all(
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7162
|
+
await Promise.all(
|
|
7163
|
+
matched.map(async (rec) => {
|
|
7164
|
+
const content = await fetchSkillContent(rec);
|
|
7165
|
+
if (content) contentMap.set(rec.slug, content);
|
|
7166
|
+
})
|
|
7167
|
+
);
|
|
7064
7168
|
const installable = matched.filter((r) => contentMap.has(r.slug));
|
|
7065
7169
|
if (!installable.length) {
|
|
7066
7170
|
spinner.fail("Could not fetch skill content.");
|
|
@@ -7097,13 +7201,17 @@ async function searchAndInstallSkills(targetPlatforms) {
|
|
|
7097
7201
|
const fingerprint = await collectFingerprint(process.cwd());
|
|
7098
7202
|
const platforms = targetPlatforms ?? detectLocalPlatforms();
|
|
7099
7203
|
const installedSkills = getInstalledSkills(platforms);
|
|
7100
|
-
const technologies = [
|
|
7101
|
-
...
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
]
|
|
7204
|
+
const technologies = [
|
|
7205
|
+
...new Set(
|
|
7206
|
+
[...fingerprint.languages, ...fingerprint.frameworks, ...extractTopDeps()].filter(Boolean)
|
|
7207
|
+
)
|
|
7208
|
+
];
|
|
7105
7209
|
if (technologies.length === 0) {
|
|
7106
|
-
console.log(
|
|
7210
|
+
console.log(
|
|
7211
|
+
chalk6.yellow(
|
|
7212
|
+
"Could not detect any languages or dependencies. Try running from a project root."
|
|
7213
|
+
)
|
|
7214
|
+
);
|
|
7107
7215
|
throw new Error("__exit__");
|
|
7108
7216
|
}
|
|
7109
7217
|
const primaryPlatform = platforms.includes("claude") ? "claude" : platforms[0];
|
|
@@ -7133,7 +7241,9 @@ async function searchAndInstallSkills(targetPlatforms) {
|
|
|
7133
7241
|
scoreSpinner.succeed("No highly relevant skills found for your specific project.");
|
|
7134
7242
|
return;
|
|
7135
7243
|
}
|
|
7136
|
-
scoreSpinner.succeed(
|
|
7244
|
+
scoreSpinner.succeed(
|
|
7245
|
+
`${results.length} relevant skill${results.length > 1 ? "s" : ""} for your project`
|
|
7246
|
+
);
|
|
7137
7247
|
} catch {
|
|
7138
7248
|
scoreSpinner.warn("Could not score relevance \u2014 showing top results");
|
|
7139
7249
|
results = newCandidates.slice(0, 20);
|
|
@@ -7143,10 +7253,12 @@ async function searchAndInstallSkills(targetPlatforms) {
|
|
|
7143
7253
|
}
|
|
7144
7254
|
const fetchSpinner = ora("Verifying skill availability...").start();
|
|
7145
7255
|
const contentMap = /* @__PURE__ */ new Map();
|
|
7146
|
-
await Promise.all(
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7256
|
+
await Promise.all(
|
|
7257
|
+
results.map(async (rec) => {
|
|
7258
|
+
const content = await fetchSkillContent(rec);
|
|
7259
|
+
if (content) contentMap.set(rec.slug, content);
|
|
7260
|
+
})
|
|
7261
|
+
);
|
|
7150
7262
|
const available = results.filter((r) => contentMap.has(r.slug));
|
|
7151
7263
|
if (!available.length) {
|
|
7152
7264
|
fetchSpinner.fail("No installable skills found \u2014 content could not be fetched.");
|
|
@@ -7194,9 +7306,13 @@ async function interactiveSelect(recs) {
|
|
|
7194
7306
|
if (hasScores) {
|
|
7195
7307
|
const scoreColor = rec.score >= 90 ? chalk6.green : rec.score >= 70 ? chalk6.yellow : chalk6.dim;
|
|
7196
7308
|
const reasonMax = Math.max(cols - prefixWidth - scoreWidth - nameWidth - 2, 20);
|
|
7197
|
-
lines.push(
|
|
7309
|
+
lines.push(
|
|
7310
|
+
` ${ptr} ${check} ${scoreColor(String(rec.score).padStart(3))} ${rec.name.padEnd(nameWidth)}${chalk6.dim(rec.reason.slice(0, reasonMax))}`
|
|
7311
|
+
);
|
|
7198
7312
|
} else {
|
|
7199
|
-
lines.push(
|
|
7313
|
+
lines.push(
|
|
7314
|
+
` ${ptr} ${check} ${rec.name.padEnd(nameWidth)}${rec.detected_technology.padEnd(16)} ${chalk6.dim(rec.source_url || "")}`
|
|
7315
|
+
);
|
|
7200
7316
|
}
|
|
7201
7317
|
}
|
|
7202
7318
|
lines.push("");
|
|
@@ -7252,7 +7368,9 @@ async function interactiveSelect(recs) {
|
|
|
7252
7368
|
console.log(chalk6.dim("\n No skills selected.\n"));
|
|
7253
7369
|
resolve3(null);
|
|
7254
7370
|
} else {
|
|
7255
|
-
resolve3(
|
|
7371
|
+
resolve3(
|
|
7372
|
+
Array.from(selected).sort().map((i) => recs[i])
|
|
7373
|
+
);
|
|
7256
7374
|
}
|
|
7257
7375
|
break;
|
|
7258
7376
|
case "q":
|
|
@@ -7321,17 +7439,25 @@ function printSkills(recs) {
|
|
|
7321
7439
|
const prefixWidth = 2;
|
|
7322
7440
|
console.log(chalk6.bold("\n Skills\n"));
|
|
7323
7441
|
if (hasScores) {
|
|
7324
|
-
console.log(
|
|
7442
|
+
console.log(
|
|
7443
|
+
" ".repeat(prefixWidth) + chalk6.dim("Score".padEnd(scoreWidth)) + chalk6.dim("Name".padEnd(nameWidth)) + chalk6.dim("Why")
|
|
7444
|
+
);
|
|
7325
7445
|
} else {
|
|
7326
|
-
console.log(
|
|
7446
|
+
console.log(
|
|
7447
|
+
" ".repeat(prefixWidth) + chalk6.dim("Name".padEnd(nameWidth)) + chalk6.dim("Technology".padEnd(18)) + chalk6.dim("Source")
|
|
7448
|
+
);
|
|
7327
7449
|
}
|
|
7328
7450
|
console.log(chalk6.dim(" " + "\u2500".repeat(Math.min(cols - 4, 90))));
|
|
7329
7451
|
for (const rec of recs) {
|
|
7330
7452
|
if (hasScores) {
|
|
7331
7453
|
const reasonMax = Math.max(cols - prefixWidth - scoreWidth - nameWidth - 2, 20);
|
|
7332
|
-
console.log(
|
|
7454
|
+
console.log(
|
|
7455
|
+
` ${String(rec.score).padStart(3)} ${rec.name.padEnd(nameWidth)}${chalk6.dim(rec.reason.slice(0, reasonMax))}`
|
|
7456
|
+
);
|
|
7333
7457
|
} else {
|
|
7334
|
-
console.log(
|
|
7458
|
+
console.log(
|
|
7459
|
+
` ${rec.name.padEnd(nameWidth)}${rec.detected_technology.padEnd(16)} ${chalk6.dim(rec.source_url || "")}`
|
|
7460
|
+
);
|
|
7335
7461
|
}
|
|
7336
7462
|
}
|
|
7337
7463
|
console.log("");
|
|
@@ -7551,11 +7677,11 @@ function countIssuePoints(issues) {
|
|
|
7551
7677
|
}
|
|
7552
7678
|
async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
|
|
7553
7679
|
const existsCache = /* @__PURE__ */ new Map();
|
|
7554
|
-
const cachedExists = (
|
|
7555
|
-
const cached = existsCache.get(
|
|
7680
|
+
const cachedExists = (path41) => {
|
|
7681
|
+
const cached = existsCache.get(path41);
|
|
7556
7682
|
if (cached !== void 0) return cached;
|
|
7557
|
-
const result = existsSync9(
|
|
7558
|
-
existsCache.set(
|
|
7683
|
+
const result = existsSync9(path41);
|
|
7684
|
+
existsCache.set(path41, result);
|
|
7559
7685
|
return result;
|
|
7560
7686
|
};
|
|
7561
7687
|
const projectStructure = collectProjectStructure(dir);
|
|
@@ -7695,7 +7821,7 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory) {
|
|
|
7695
7821
|
|
|
7696
7822
|
// src/lib/debug-report.ts
|
|
7697
7823
|
import fs25 from "fs";
|
|
7698
|
-
import
|
|
7824
|
+
import path22 from "path";
|
|
7699
7825
|
var DebugReport = class {
|
|
7700
7826
|
sections = [];
|
|
7701
7827
|
startTime;
|
|
@@ -7764,7 +7890,7 @@ var DebugReport = class {
|
|
|
7764
7890
|
lines.push(`| **Total** | **${formatMs(totalMs)}** |`);
|
|
7765
7891
|
lines.push("");
|
|
7766
7892
|
}
|
|
7767
|
-
const dir =
|
|
7893
|
+
const dir = path22.dirname(outputPath);
|
|
7768
7894
|
if (!fs25.existsSync(dir)) {
|
|
7769
7895
|
fs25.mkdirSync(dir, { recursive: true });
|
|
7770
7896
|
}
|
|
@@ -8450,7 +8576,11 @@ function formatWhatChanged(setup) {
|
|
|
8450
8576
|
lines.push(`${action} AGENTS.md`);
|
|
8451
8577
|
}
|
|
8452
8578
|
const allSkills = [];
|
|
8453
|
-
for (const [
|
|
8579
|
+
for (const [_platform, obj] of [
|
|
8580
|
+
["claude", claude],
|
|
8581
|
+
["codex", codex],
|
|
8582
|
+
["cursor", cursor]
|
|
8583
|
+
]) {
|
|
8454
8584
|
const skills = obj?.skills;
|
|
8455
8585
|
if (Array.isArray(skills)) {
|
|
8456
8586
|
for (const s of skills) allSkills.push(s.name);
|
|
@@ -8467,7 +8597,9 @@ function formatWhatChanged(setup) {
|
|
|
8467
8597
|
}
|
|
8468
8598
|
const deletions = setup.deletions;
|
|
8469
8599
|
if (Array.isArray(deletions) && deletions.length > 0) {
|
|
8470
|
-
lines.push(
|
|
8600
|
+
lines.push(
|
|
8601
|
+
`Removing ${deletions.length} file${deletions.length === 1 ? "" : "s"}: ${deletions.map((d) => d.filePath).join(", ")}`
|
|
8602
|
+
);
|
|
8471
8603
|
}
|
|
8472
8604
|
return lines;
|
|
8473
8605
|
}
|
|
@@ -8565,7 +8697,9 @@ function printSetupSummary(setup) {
|
|
|
8565
8697
|
console.log("");
|
|
8566
8698
|
}
|
|
8567
8699
|
}
|
|
8568
|
-
console.log(
|
|
8700
|
+
console.log(
|
|
8701
|
+
` ${chalk12.green("+")} ${chalk12.dim("new")} ${chalk12.yellow("~")} ${chalk12.dim("modified")} ${chalk12.red("-")} ${chalk12.dim("removed")}`
|
|
8702
|
+
);
|
|
8569
8703
|
console.log("");
|
|
8570
8704
|
}
|
|
8571
8705
|
function displayTokenUsage() {
|
|
@@ -8580,11 +8714,17 @@ function displayTokenUsage() {
|
|
|
8580
8714
|
for (const m of summary) {
|
|
8581
8715
|
totalIn += m.inputTokens;
|
|
8582
8716
|
totalOut += m.outputTokens;
|
|
8583
|
-
const cacheInfo = m.cacheReadTokens > 0 || m.cacheWriteTokens > 0 ? chalk12.dim(
|
|
8584
|
-
|
|
8717
|
+
const cacheInfo = m.cacheReadTokens > 0 || m.cacheWriteTokens > 0 ? chalk12.dim(
|
|
8718
|
+
` (cache: ${m.cacheReadTokens.toLocaleString()} read, ${m.cacheWriteTokens.toLocaleString()} write)`
|
|
8719
|
+
) : "";
|
|
8720
|
+
console.log(
|
|
8721
|
+
` ${chalk12.dim(m.model)}: ${m.inputTokens.toLocaleString()} in / ${m.outputTokens.toLocaleString()} out (${m.calls} call${m.calls === 1 ? "" : "s"})${cacheInfo}`
|
|
8722
|
+
);
|
|
8585
8723
|
}
|
|
8586
8724
|
if (summary.length > 1) {
|
|
8587
|
-
console.log(
|
|
8725
|
+
console.log(
|
|
8726
|
+
` ${chalk12.dim("Total")}: ${totalIn.toLocaleString()} in / ${totalOut.toLocaleString()} out`
|
|
8727
|
+
);
|
|
8588
8728
|
}
|
|
8589
8729
|
console.log("");
|
|
8590
8730
|
}
|
|
@@ -8593,9 +8733,9 @@ function displayTokenUsage() {
|
|
|
8593
8733
|
init_config();
|
|
8594
8734
|
import chalk13 from "chalk";
|
|
8595
8735
|
import fs30 from "fs";
|
|
8596
|
-
import
|
|
8736
|
+
import path24 from "path";
|
|
8597
8737
|
function isFirstRun(dir) {
|
|
8598
|
-
const caliberDir =
|
|
8738
|
+
const caliberDir = path24.join(dir, ".caliber");
|
|
8599
8739
|
try {
|
|
8600
8740
|
const stat = fs30.statSync(caliberDir);
|
|
8601
8741
|
return !stat.isDirectory();
|
|
@@ -8665,7 +8805,7 @@ function ensurePermissions(fingerprint) {
|
|
|
8665
8805
|
}
|
|
8666
8806
|
function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
8667
8807
|
try {
|
|
8668
|
-
const logPath =
|
|
8808
|
+
const logPath = path24.join(process.cwd(), ".caliber", "error-log.md");
|
|
8669
8809
|
const lines = [
|
|
8670
8810
|
`# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
8671
8811
|
"",
|
|
@@ -8678,7 +8818,7 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
|
8678
8818
|
lines.push("## Error", "```", error, "```", "");
|
|
8679
8819
|
}
|
|
8680
8820
|
lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
|
|
8681
|
-
fs30.mkdirSync(
|
|
8821
|
+
fs30.mkdirSync(path24.join(process.cwd(), ".caliber"), { recursive: true });
|
|
8682
8822
|
fs30.writeFileSync(logPath, lines.join("\n"));
|
|
8683
8823
|
console.log(chalk13.dim(`
|
|
8684
8824
|
Error log written to .caliber/error-log.md`));
|
|
@@ -8731,12 +8871,12 @@ ${JSON.stringify(checkList, null, 2)}`,
|
|
|
8731
8871
|
|
|
8732
8872
|
// src/scoring/history.ts
|
|
8733
8873
|
import fs31 from "fs";
|
|
8734
|
-
import
|
|
8874
|
+
import path25 from "path";
|
|
8735
8875
|
var HISTORY_FILE = "score-history.jsonl";
|
|
8736
8876
|
var MAX_ENTRIES = 500;
|
|
8737
8877
|
var TRIM_THRESHOLD = MAX_ENTRIES + 50;
|
|
8738
8878
|
function historyFilePath() {
|
|
8739
|
-
return
|
|
8879
|
+
return path25.join(CALIBER_DIR, HISTORY_FILE);
|
|
8740
8880
|
}
|
|
8741
8881
|
function recordScore(result, trigger) {
|
|
8742
8882
|
const entry = {
|
|
@@ -8801,14 +8941,16 @@ async function initCommand(options) {
|
|
|
8801
8941
|
const bin = resolveCaliber();
|
|
8802
8942
|
const firstRun = isFirstRun(process.cwd());
|
|
8803
8943
|
if (firstRun) {
|
|
8804
|
-
console.log(
|
|
8944
|
+
console.log(
|
|
8945
|
+
brand.bold(`
|
|
8805
8946
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
8806
8947
|
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
8807
8948
|
\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
8808
8949
|
\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
8809
8950
|
\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
8810
8951
|
\u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
|
|
8811
|
-
`)
|
|
8952
|
+
`)
|
|
8953
|
+
);
|
|
8812
8954
|
console.log(chalk14.dim(" Scan your project and generate tailored config files for"));
|
|
8813
8955
|
console.log(chalk14.dim(" Claude Code, Cursor, Codex, and GitHub Copilot.\n"));
|
|
8814
8956
|
console.log(title.bold(" How it works:\n"));
|
|
@@ -8822,7 +8964,11 @@ async function initCommand(options) {
|
|
|
8822
8964
|
const platforms = detectPlatforms();
|
|
8823
8965
|
if (!platforms.claude && !platforms.cursor && !platforms.codex) {
|
|
8824
8966
|
console.log(chalk14.yellow(" \u26A0 No supported AI platforms detected (Claude, Cursor, Codex)."));
|
|
8825
|
-
console.log(
|
|
8967
|
+
console.log(
|
|
8968
|
+
chalk14.yellow(
|
|
8969
|
+
" Caliber will still generate config files, but they won't be auto-installed.\n"
|
|
8970
|
+
)
|
|
8971
|
+
);
|
|
8826
8972
|
}
|
|
8827
8973
|
const report = options.debugReport ? new DebugReport() : null;
|
|
8828
8974
|
console.log(title.bold(" Step 1/4 \u2014 Connect\n"));
|
|
@@ -8846,9 +8992,12 @@ async function initCommand(options) {
|
|
|
8846
8992
|
console.log(chalk14.dim(modelLine + "\n"));
|
|
8847
8993
|
if (report) {
|
|
8848
8994
|
report.markStep("Provider connection");
|
|
8849
|
-
report.addSection(
|
|
8995
|
+
report.addSection(
|
|
8996
|
+
"LLM Provider",
|
|
8997
|
+
`- **Provider**: ${config.provider}
|
|
8850
8998
|
- **Model**: ${displayModel}
|
|
8851
|
-
- **Fast model**: ${fastModel || "none"}`
|
|
8999
|
+
- **Fast model**: ${fastModel || "none"}`
|
|
9000
|
+
);
|
|
8852
9001
|
}
|
|
8853
9002
|
await validateModel({ fast: true });
|
|
8854
9003
|
let targetAgent;
|
|
@@ -8865,21 +9014,29 @@ async function initCommand(options) {
|
|
|
8865
9014
|
console.log(chalk14.dim(` Target: ${targetAgent.join(", ")}
|
|
8866
9015
|
`));
|
|
8867
9016
|
trackInitAgentSelected(targetAgent, agentAutoDetected);
|
|
8868
|
-
|
|
9017
|
+
const baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
8869
9018
|
console.log(chalk14.dim("\n Current config score:"));
|
|
8870
9019
|
displayScoreSummary(baselineScore);
|
|
8871
9020
|
if (options.verbose) {
|
|
8872
9021
|
for (const c of baselineScore.checks) {
|
|
8873
|
-
log(
|
|
9022
|
+
log(
|
|
9023
|
+
options.verbose,
|
|
9024
|
+
` ${c.passed ? "\u2713" : "\u2717"} ${c.name}: ${c.earnedPoints}/${c.maxPoints}${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`
|
|
9025
|
+
);
|
|
8874
9026
|
}
|
|
8875
9027
|
}
|
|
8876
9028
|
if (report) {
|
|
8877
9029
|
report.markStep("Baseline scoring");
|
|
8878
|
-
report.addSection(
|
|
9030
|
+
report.addSection(
|
|
9031
|
+
"Scoring: Baseline",
|
|
9032
|
+
`**Score**: ${baselineScore.score}/100
|
|
8879
9033
|
|
|
8880
9034
|
| Check | Passed | Points | Max |
|
|
8881
9035
|
|-------|--------|--------|-----|
|
|
8882
|
-
` + baselineScore.checks.map(
|
|
9036
|
+
` + baselineScore.checks.map(
|
|
9037
|
+
(c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`
|
|
9038
|
+
).join("\n")
|
|
9039
|
+
);
|
|
8883
9040
|
report.addSection("Generation: Target Agents", targetAgent.join(", "));
|
|
8884
9041
|
}
|
|
8885
9042
|
const hasExistingConfig = !!(baselineScore.checks.some((c) => c.id === "claude_md_exists" && c.passed) || baselineScore.checks.some((c) => c.id === "cursorrules_exists" && c.passed));
|
|
@@ -8896,7 +9053,9 @@ async function initCommand(options) {
|
|
|
8896
9053
|
if (hasExistingConfig && baselineScore.score === 100) {
|
|
8897
9054
|
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, true);
|
|
8898
9055
|
console.log(chalk14.bold.green(" Your config is already optimal \u2014 nothing to change.\n"));
|
|
8899
|
-
console.log(
|
|
9056
|
+
console.log(
|
|
9057
|
+
chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to regenerate anyway.\n")
|
|
9058
|
+
);
|
|
8900
9059
|
if (!options.force) return;
|
|
8901
9060
|
}
|
|
8902
9061
|
const allFailingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
|
|
@@ -8912,7 +9071,9 @@ async function initCommand(options) {
|
|
|
8912
9071
|
}
|
|
8913
9072
|
}
|
|
8914
9073
|
console.log("");
|
|
8915
|
-
console.log(
|
|
9074
|
+
console.log(
|
|
9075
|
+
chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to regenerate anyway.\n")
|
|
9076
|
+
);
|
|
8916
9077
|
return;
|
|
8917
9078
|
}
|
|
8918
9079
|
console.log(title.bold(" Step 2/4 \u2014 Engine\n"));
|
|
@@ -8931,8 +9092,14 @@ async function initCommand(options) {
|
|
|
8931
9092
|
const TASK_STACK = display.add("Detecting project stack", { pipelineLabel: "Scan" });
|
|
8932
9093
|
const TASK_CONFIG = display.add("Generating configs", { depth: 1, pipelineLabel: "Generate" });
|
|
8933
9094
|
const TASK_SKILLS_GEN = display.add("Generating skills", { depth: 2, pipelineLabel: "Skills" });
|
|
8934
|
-
const TASK_SKILLS_SEARCH = display.add("Searching community skills", {
|
|
8935
|
-
|
|
9095
|
+
const TASK_SKILLS_SEARCH = display.add("Searching community skills", {
|
|
9096
|
+
depth: 1,
|
|
9097
|
+
pipelineLabel: "Search",
|
|
9098
|
+
pipelineRow: 1
|
|
9099
|
+
});
|
|
9100
|
+
const TASK_SCORE_REFINE = display.add("Validating & refining config", {
|
|
9101
|
+
pipelineLabel: "Validate"
|
|
9102
|
+
});
|
|
8936
9103
|
display.start();
|
|
8937
9104
|
display.enableWaitingContent();
|
|
8938
9105
|
try {
|
|
@@ -8942,21 +9109,39 @@ async function initCommand(options) {
|
|
|
8942
9109
|
const stackSummary = stackParts.join(", ") || "no languages";
|
|
8943
9110
|
const largeRepoNote = fingerprint.fileTree.length > 5e3 ? ` (${fingerprint.fileTree.length.toLocaleString()} files, smart sampling active)` : "";
|
|
8944
9111
|
display.update(TASK_STACK, "done", stackSummary + largeRepoNote);
|
|
8945
|
-
trackInitProjectDiscovered(
|
|
8946
|
-
|
|
9112
|
+
trackInitProjectDiscovered(
|
|
9113
|
+
fingerprint.languages.length,
|
|
9114
|
+
fingerprint.frameworks.length,
|
|
9115
|
+
fingerprint.fileTree.length
|
|
9116
|
+
);
|
|
9117
|
+
log(
|
|
9118
|
+
options.verbose,
|
|
9119
|
+
`Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`
|
|
9120
|
+
);
|
|
8947
9121
|
const cliSources = options.source || [];
|
|
8948
9122
|
const workspaces = getDetectedWorkspaces(process.cwd());
|
|
8949
9123
|
const sources2 = resolveAllSources(process.cwd(), cliSources, workspaces);
|
|
8950
9124
|
if (sources2.length > 0) {
|
|
8951
9125
|
fingerprint.sources = sources2;
|
|
8952
|
-
log(
|
|
9126
|
+
log(
|
|
9127
|
+
options.verbose,
|
|
9128
|
+
`Sources: ${sources2.length} resolved (${sources2.map((s) => s.name).join(", ")})`
|
|
9129
|
+
);
|
|
8953
9130
|
}
|
|
8954
9131
|
if (report) {
|
|
8955
|
-
report.addJson("Fingerprint: Git", {
|
|
9132
|
+
report.addJson("Fingerprint: Git", {
|
|
9133
|
+
remote: fingerprint.gitRemoteUrl,
|
|
9134
|
+
packageName: fingerprint.packageName
|
|
9135
|
+
});
|
|
8956
9136
|
report.addCodeBlock("Fingerprint: File Tree", fingerprint.fileTree.join("\n"));
|
|
8957
|
-
report.addJson("Fingerprint: Detected Stack", {
|
|
9137
|
+
report.addJson("Fingerprint: Detected Stack", {
|
|
9138
|
+
languages: fingerprint.languages,
|
|
9139
|
+
frameworks: fingerprint.frameworks,
|
|
9140
|
+
tools: fingerprint.tools
|
|
9141
|
+
});
|
|
8958
9142
|
report.addJson("Fingerprint: Existing Configs", fingerprint.existingConfigs);
|
|
8959
|
-
if (fingerprint.codeAnalysis)
|
|
9143
|
+
if (fingerprint.codeAnalysis)
|
|
9144
|
+
report.addJson("Fingerprint: Code Analysis", fingerprint.codeAnalysis);
|
|
8960
9145
|
}
|
|
8961
9146
|
const isEmpty = fingerprint.fileTree.length < 3;
|
|
8962
9147
|
if (isEmpty) {
|
|
@@ -8987,13 +9172,26 @@ async function initCommand(options) {
|
|
|
8987
9172
|
let passingChecks;
|
|
8988
9173
|
let currentScore;
|
|
8989
9174
|
if (hasExistingConfig && localBaseline.score >= 95 && !options.force) {
|
|
8990
|
-
const currentLlmFixable = localBaseline.checks.filter(
|
|
8991
|
-
|
|
9175
|
+
const currentLlmFixable = localBaseline.checks.filter(
|
|
9176
|
+
(c) => !c.passed && c.maxPoints > 0 && !NON_LLM_CHECKS.has(c.id)
|
|
9177
|
+
);
|
|
9178
|
+
failingChecks = currentLlmFixable.map((c) => ({
|
|
9179
|
+
name: c.name,
|
|
9180
|
+
suggestion: c.suggestion,
|
|
9181
|
+
fix: c.fix
|
|
9182
|
+
}));
|
|
8992
9183
|
passingChecks = localBaseline.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
|
|
8993
9184
|
currentScore = localBaseline.score;
|
|
8994
9185
|
}
|
|
8995
9186
|
if (report) {
|
|
8996
|
-
const fullPrompt = buildGeneratePrompt(
|
|
9187
|
+
const fullPrompt = buildGeneratePrompt(
|
|
9188
|
+
fingerprint,
|
|
9189
|
+
targetAgent,
|
|
9190
|
+
fingerprint.description,
|
|
9191
|
+
failingChecks,
|
|
9192
|
+
currentScore,
|
|
9193
|
+
passingChecks
|
|
9194
|
+
);
|
|
8997
9195
|
report.addCodeBlock("Generation: Full LLM Prompt", fullPrompt);
|
|
8998
9196
|
}
|
|
8999
9197
|
const result = await generateSetup(
|
|
@@ -9113,7 +9311,10 @@ async function initCommand(options) {
|
|
|
9113
9311
|
if (rawOutput) report.addCodeBlock("Generation: Raw LLM Response", rawOutput);
|
|
9114
9312
|
report.addJson("Generation: Parsed Config", generatedSetup);
|
|
9115
9313
|
}
|
|
9116
|
-
log(
|
|
9314
|
+
log(
|
|
9315
|
+
options.verbose,
|
|
9316
|
+
`Generation completed: ${elapsedMs}ms, stopReason: ${genStopReason || "end_turn"}`
|
|
9317
|
+
);
|
|
9117
9318
|
console.log(title.bold(" Step 3/4 \u2014 Review\n"));
|
|
9118
9319
|
const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
|
|
9119
9320
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
@@ -9125,10 +9326,18 @@ async function initCommand(options) {
|
|
|
9125
9326
|
}
|
|
9126
9327
|
console.log("");
|
|
9127
9328
|
}
|
|
9128
|
-
console.log(
|
|
9329
|
+
console.log(
|
|
9330
|
+
chalk14.dim(
|
|
9331
|
+
` ${chalk14.green(`${staged.newFiles} new`)} / ${chalk14.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}`
|
|
9332
|
+
)
|
|
9333
|
+
);
|
|
9129
9334
|
if (skillSearchResult.results.length > 0) {
|
|
9130
|
-
console.log(
|
|
9131
|
-
|
|
9335
|
+
console.log(
|
|
9336
|
+
chalk14.dim(
|
|
9337
|
+
` ${chalk14.cyan(`${skillSearchResult.results.length}`)} community skills available to install
|
|
9338
|
+
`
|
|
9339
|
+
)
|
|
9340
|
+
);
|
|
9132
9341
|
} else {
|
|
9133
9342
|
console.log("");
|
|
9134
9343
|
}
|
|
@@ -9164,8 +9373,12 @@ async function initCommand(options) {
|
|
|
9164
9373
|
}
|
|
9165
9374
|
const updatedFiles = collectSetupFiles(generatedSetup, targetAgent);
|
|
9166
9375
|
const restaged = stageFiles(updatedFiles, process.cwd());
|
|
9167
|
-
console.log(
|
|
9168
|
-
|
|
9376
|
+
console.log(
|
|
9377
|
+
chalk14.dim(
|
|
9378
|
+
` ${chalk14.green(`${restaged.newFiles} new`)} / ${chalk14.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
|
|
9379
|
+
`
|
|
9380
|
+
)
|
|
9381
|
+
);
|
|
9169
9382
|
printSetupSummary(generatedSetup);
|
|
9170
9383
|
const { openReview: openRev } = await Promise.resolve().then(() => (init_review(), review_exports));
|
|
9171
9384
|
await openRev("terminal", restaged.stagedFiles);
|
|
@@ -9192,7 +9405,8 @@ async function initCommand(options) {
|
|
|
9192
9405
|
const agentRefs = [];
|
|
9193
9406
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
9194
9407
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
9195
|
-
if (agentRefs.length === 0)
|
|
9408
|
+
if (agentRefs.length === 0)
|
|
9409
|
+
agentRefs.push("See CLAUDE.md and .cursor/rules/ for agent configurations.");
|
|
9196
9410
|
const stubContent = `# AGENTS.md
|
|
9197
9411
|
|
|
9198
9412
|
This project uses AI coding agents configured by [Caliber](https://github.com/caliber-ai-org/ai-setup).
|
|
@@ -9239,31 +9453,49 @@ ${agentRefs.join(" ")}
|
|
|
9239
9453
|
if (afterScore.score < baselineScore.score) {
|
|
9240
9454
|
trackInitScoreRegression(baselineScore.score, afterScore.score);
|
|
9241
9455
|
console.log("");
|
|
9242
|
-
console.log(
|
|
9456
|
+
console.log(
|
|
9457
|
+
chalk14.yellow(
|
|
9458
|
+
` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`
|
|
9459
|
+
)
|
|
9460
|
+
);
|
|
9243
9461
|
try {
|
|
9244
9462
|
const { restored, removed } = undoSetup();
|
|
9245
9463
|
if (restored.length > 0 || removed.length > 0) {
|
|
9246
|
-
console.log(
|
|
9464
|
+
console.log(
|
|
9465
|
+
chalk14.dim(
|
|
9466
|
+
` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`
|
|
9467
|
+
)
|
|
9468
|
+
);
|
|
9247
9469
|
}
|
|
9248
9470
|
} catch {
|
|
9249
9471
|
}
|
|
9250
|
-
console.log(
|
|
9472
|
+
console.log(
|
|
9473
|
+
chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to override.\n")
|
|
9474
|
+
);
|
|
9251
9475
|
return;
|
|
9252
9476
|
}
|
|
9253
9477
|
if (report) {
|
|
9254
9478
|
report.markStep("Post-write scoring");
|
|
9255
|
-
report.addSection(
|
|
9479
|
+
report.addSection(
|
|
9480
|
+
"Scoring: Post-Write",
|
|
9481
|
+
`**Score**: ${afterScore.score}/100 (delta: ${afterScore.score - baselineScore.score >= 0 ? "+" : ""}${afterScore.score - baselineScore.score})
|
|
9256
9482
|
|
|
9257
9483
|
| Check | Passed | Points | Max |
|
|
9258
9484
|
|-------|--------|--------|-----|
|
|
9259
|
-
` + afterScore.checks.map(
|
|
9485
|
+
` + afterScore.checks.map(
|
|
9486
|
+
(c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`
|
|
9487
|
+
).join("\n")
|
|
9488
|
+
);
|
|
9260
9489
|
}
|
|
9261
9490
|
recordScore(afterScore, "init");
|
|
9262
9491
|
displayScoreDelta(baselineScore, afterScore);
|
|
9263
9492
|
if (options.verbose) {
|
|
9264
9493
|
log(options.verbose, `Final score: ${afterScore.score}/100`);
|
|
9265
9494
|
for (const c of afterScore.checks.filter((ch) => !ch.passed)) {
|
|
9266
|
-
log(
|
|
9495
|
+
log(
|
|
9496
|
+
options.verbose,
|
|
9497
|
+
` Still failing: ${c.name} (${c.earnedPoints}/${c.maxPoints})${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`
|
|
9498
|
+
);
|
|
9267
9499
|
}
|
|
9268
9500
|
}
|
|
9269
9501
|
let communitySkillsInstalled = 0;
|
|
@@ -9277,7 +9509,9 @@ ${agentRefs.join(" ")}
|
|
|
9277
9509
|
}
|
|
9278
9510
|
}
|
|
9279
9511
|
console.log("");
|
|
9280
|
-
console.log(
|
|
9512
|
+
console.log(
|
|
9513
|
+
` ${chalk14.green("\u2713")} Docs auto-refresh ${chalk14.dim(`agents run ${resolveCaliber()} refresh before commits`)}`
|
|
9514
|
+
);
|
|
9281
9515
|
trackInitHookSelected("config-instructions");
|
|
9282
9516
|
const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
|
|
9283
9517
|
let enableLearn = false;
|
|
@@ -9288,17 +9522,24 @@ ${agentRefs.join(" ")}
|
|
|
9288
9522
|
if (enableLearn) {
|
|
9289
9523
|
if (targetAgent.includes("claude")) {
|
|
9290
9524
|
const r = installLearningHooks();
|
|
9291
|
-
if (r.installed)
|
|
9292
|
-
|
|
9525
|
+
if (r.installed)
|
|
9526
|
+
console.log(` ${chalk14.green("\u2713")} Learning hooks installed for Claude Code`);
|
|
9527
|
+
else if (r.alreadyInstalled)
|
|
9528
|
+
console.log(chalk14.dim(" Claude Code learning hooks already installed"));
|
|
9293
9529
|
}
|
|
9294
9530
|
if (targetAgent.includes("cursor")) {
|
|
9295
9531
|
const r = installCursorLearningHooks();
|
|
9296
9532
|
if (r.installed) console.log(` ${chalk14.green("\u2713")} Learning hooks installed for Cursor`);
|
|
9297
|
-
else if (r.alreadyInstalled)
|
|
9533
|
+
else if (r.alreadyInstalled)
|
|
9534
|
+
console.log(chalk14.dim(" Cursor learning hooks already installed"));
|
|
9298
9535
|
}
|
|
9299
|
-
console.log(
|
|
9536
|
+
console.log(
|
|
9537
|
+
chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} learn status`) + chalk14.dim(" to see insights")
|
|
9538
|
+
);
|
|
9300
9539
|
} else {
|
|
9301
|
-
console.log(
|
|
9540
|
+
console.log(
|
|
9541
|
+
chalk14.dim(" Skipped. Run ") + chalk14.hex("#83D1EB")(`${bin} learn install`) + chalk14.dim(" later to enable.")
|
|
9542
|
+
);
|
|
9302
9543
|
}
|
|
9303
9544
|
} else {
|
|
9304
9545
|
enableLearn = true;
|
|
@@ -9307,28 +9548,46 @@ ${agentRefs.join(" ")}
|
|
|
9307
9548
|
}
|
|
9308
9549
|
}
|
|
9309
9550
|
console.log(chalk14.bold.green("\n Configuration complete!"));
|
|
9310
|
-
console.log(
|
|
9311
|
-
|
|
9551
|
+
console.log(
|
|
9552
|
+
chalk14.dim(" Your AI agents now understand your project's architecture, build commands,")
|
|
9553
|
+
);
|
|
9554
|
+
console.log(
|
|
9555
|
+
chalk14.dim(" testing patterns, and conventions. All changes are backed up automatically.\n")
|
|
9556
|
+
);
|
|
9312
9557
|
const done = chalk14.green("\u2713");
|
|
9313
9558
|
const skip = chalk14.dim("\u2013");
|
|
9314
9559
|
console.log(chalk14.bold(" What was configured:\n"));
|
|
9315
|
-
console.log(
|
|
9316
|
-
|
|
9560
|
+
console.log(
|
|
9561
|
+
` ${done} Config generated ${title(`${bin} score`)} ${chalk14.dim("for full breakdown")}`
|
|
9562
|
+
);
|
|
9563
|
+
console.log(
|
|
9564
|
+
` ${done} Docs auto-refresh ${chalk14.dim(`agents run ${bin} refresh before commits`)}`
|
|
9565
|
+
);
|
|
9317
9566
|
if (hasLearnableAgent) {
|
|
9318
9567
|
if (enableLearn) {
|
|
9319
|
-
console.log(
|
|
9568
|
+
console.log(
|
|
9569
|
+
` ${done} Session learning ${chalk14.dim("agent learns from your feedback")}`
|
|
9570
|
+
);
|
|
9320
9571
|
} else {
|
|
9321
|
-
console.log(
|
|
9572
|
+
console.log(
|
|
9573
|
+
` ${skip} Session learning ${title(`${bin} learn install`)} to enable later`
|
|
9574
|
+
);
|
|
9322
9575
|
}
|
|
9323
9576
|
}
|
|
9324
9577
|
if (communitySkillsInstalled > 0) {
|
|
9325
|
-
console.log(
|
|
9578
|
+
console.log(
|
|
9579
|
+
` ${done} Community skills ${chalk14.dim(`${communitySkillsInstalled} skill${communitySkillsInstalled > 1 ? "s" : ""} installed for your stack`)}`
|
|
9580
|
+
);
|
|
9326
9581
|
} else if (skillSearchResult.results.length > 0) {
|
|
9327
9582
|
console.log(` ${skip} Community skills ${chalk14.dim("available but skipped")}`);
|
|
9328
9583
|
}
|
|
9329
9584
|
console.log(chalk14.bold("\n Explore next:\n"));
|
|
9330
|
-
console.log(
|
|
9331
|
-
|
|
9585
|
+
console.log(
|
|
9586
|
+
` ${title(`${bin} skills`)} Find more community skills as your codebase evolves`
|
|
9587
|
+
);
|
|
9588
|
+
console.log(
|
|
9589
|
+
` ${title(`${bin} score`)} See the full scoring breakdown with improvement tips`
|
|
9590
|
+
);
|
|
9332
9591
|
console.log(` ${title(`${bin} undo`)} Revert all changes from this run`);
|
|
9333
9592
|
console.log("");
|
|
9334
9593
|
if (options.showTokens) {
|
|
@@ -9336,10 +9595,12 @@ ${agentRefs.join(" ")}
|
|
|
9336
9595
|
}
|
|
9337
9596
|
if (report) {
|
|
9338
9597
|
report.markStep("Finished");
|
|
9339
|
-
const reportPath =
|
|
9598
|
+
const reportPath = path26.join(process.cwd(), ".caliber", "debug-report.md");
|
|
9340
9599
|
report.write(reportPath);
|
|
9341
|
-
console.log(
|
|
9342
|
-
`))
|
|
9600
|
+
console.log(
|
|
9601
|
+
chalk14.dim(` Debug report written to ${path26.relative(process.cwd(), reportPath)}
|
|
9602
|
+
`)
|
|
9603
|
+
);
|
|
9343
9604
|
}
|
|
9344
9605
|
}
|
|
9345
9606
|
|
|
@@ -9565,30 +9826,39 @@ async function regenerateCommand(options) {
|
|
|
9565
9826
|
// src/commands/score.ts
|
|
9566
9827
|
import fs34 from "fs";
|
|
9567
9828
|
import os7 from "os";
|
|
9568
|
-
import
|
|
9829
|
+
import path27 from "path";
|
|
9569
9830
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
9570
9831
|
import chalk18 from "chalk";
|
|
9571
9832
|
init_resolve_caliber();
|
|
9572
9833
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS.md"];
|
|
9573
9834
|
var CONFIG_DIRS = [".claude", ".cursor"];
|
|
9574
9835
|
function scoreBaseRef(ref, target) {
|
|
9575
|
-
if (!/^[\w
|
|
9576
|
-
const tmpDir = fs34.mkdtempSync(
|
|
9836
|
+
if (!/^[\w.\-/~^@{}]+$/.test(ref)) return null;
|
|
9837
|
+
const tmpDir = fs34.mkdtempSync(path27.join(os7.tmpdir(), "caliber-compare-"));
|
|
9577
9838
|
try {
|
|
9578
9839
|
for (const file of CONFIG_FILES) {
|
|
9579
9840
|
try {
|
|
9580
|
-
const content = execFileSync2("git", ["show", `${ref}:${file}`], {
|
|
9581
|
-
|
|
9841
|
+
const content = execFileSync2("git", ["show", `${ref}:${file}`], {
|
|
9842
|
+
encoding: "utf-8",
|
|
9843
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
9844
|
+
});
|
|
9845
|
+
fs34.writeFileSync(path27.join(tmpDir, file), content);
|
|
9582
9846
|
} catch {
|
|
9583
9847
|
}
|
|
9584
9848
|
}
|
|
9585
9849
|
for (const dir of CONFIG_DIRS) {
|
|
9586
9850
|
try {
|
|
9587
|
-
const files = execFileSync2("git", ["ls-tree", "-r", "--name-only", ref, `${dir}/`], {
|
|
9851
|
+
const files = execFileSync2("git", ["ls-tree", "-r", "--name-only", ref, `${dir}/`], {
|
|
9852
|
+
encoding: "utf-8",
|
|
9853
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
9854
|
+
}).trim().split("\n").filter(Boolean);
|
|
9588
9855
|
for (const file of files) {
|
|
9589
|
-
const filePath =
|
|
9590
|
-
fs34.mkdirSync(
|
|
9591
|
-
const content = execFileSync2("git", ["show", `${ref}:${file}`], {
|
|
9856
|
+
const filePath = path27.join(tmpDir, file);
|
|
9857
|
+
fs34.mkdirSync(path27.dirname(filePath), { recursive: true });
|
|
9858
|
+
const content = execFileSync2("git", ["show", `${ref}:${file}`], {
|
|
9859
|
+
encoding: "utf-8",
|
|
9860
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
9861
|
+
});
|
|
9592
9862
|
fs34.writeFileSync(filePath, content);
|
|
9593
9863
|
}
|
|
9594
9864
|
} catch {
|
|
@@ -9611,13 +9881,25 @@ async function scoreCommand(options) {
|
|
|
9611
9881
|
if (options.compare) {
|
|
9612
9882
|
const baseResult = scoreBaseRef(options.compare, target);
|
|
9613
9883
|
if (!baseResult) {
|
|
9614
|
-
console.error(
|
|
9884
|
+
console.error(
|
|
9885
|
+
chalk18.red(`Could not score ref "${options.compare}" \u2014 branch or ref not found.`)
|
|
9886
|
+
);
|
|
9615
9887
|
process.exitCode = 1;
|
|
9616
9888
|
return;
|
|
9617
9889
|
}
|
|
9618
9890
|
const delta = result.score - baseResult.score;
|
|
9619
9891
|
if (options.json) {
|
|
9620
|
-
console.log(
|
|
9892
|
+
console.log(
|
|
9893
|
+
JSON.stringify(
|
|
9894
|
+
{
|
|
9895
|
+
current: result,
|
|
9896
|
+
base: { score: baseResult.score, grade: baseResult.grade, ref: options.compare },
|
|
9897
|
+
delta
|
|
9898
|
+
},
|
|
9899
|
+
null,
|
|
9900
|
+
2
|
|
9901
|
+
)
|
|
9902
|
+
);
|
|
9621
9903
|
return;
|
|
9622
9904
|
}
|
|
9623
9905
|
if (options.quiet) {
|
|
@@ -9629,9 +9911,13 @@ async function scoreCommand(options) {
|
|
|
9629
9911
|
const separator2 = chalk18.gray(" " + "\u2500".repeat(53));
|
|
9630
9912
|
console.log(separator2);
|
|
9631
9913
|
if (delta > 0) {
|
|
9632
|
-
console.log(
|
|
9914
|
+
console.log(
|
|
9915
|
+
chalk18.green(` +${delta}`) + chalk18.gray(` from ${options.compare} (${baseResult.score}/100)`)
|
|
9916
|
+
);
|
|
9633
9917
|
} else if (delta < 0) {
|
|
9634
|
-
console.log(
|
|
9918
|
+
console.log(
|
|
9919
|
+
chalk18.red(` ${delta}`) + chalk18.gray(` from ${options.compare} (${baseResult.score}/100)`)
|
|
9920
|
+
);
|
|
9635
9921
|
} else {
|
|
9636
9922
|
console.log(chalk18.gray(` No change from ${options.compare} (${baseResult.score}/100)`));
|
|
9637
9923
|
}
|
|
@@ -9651,18 +9937,24 @@ async function scoreCommand(options) {
|
|
|
9651
9937
|
console.log(separator);
|
|
9652
9938
|
const bin = resolveCaliber();
|
|
9653
9939
|
if (result.score < 40) {
|
|
9654
|
-
console.log(
|
|
9940
|
+
console.log(
|
|
9941
|
+
chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(`${bin} init`) + chalk18.gray(" to generate a complete, optimized config.")
|
|
9942
|
+
);
|
|
9655
9943
|
} else if (result.score < 70) {
|
|
9656
|
-
console.log(
|
|
9944
|
+
console.log(
|
|
9945
|
+
chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(`${bin} init`) + chalk18.gray(" to improve your config.")
|
|
9946
|
+
);
|
|
9657
9947
|
} else {
|
|
9658
|
-
console.log(
|
|
9948
|
+
console.log(
|
|
9949
|
+
chalk18.green(" Looking good!") + chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(`${bin} regenerate`) + chalk18.gray(" to rebuild from scratch.")
|
|
9950
|
+
);
|
|
9659
9951
|
}
|
|
9660
9952
|
console.log("");
|
|
9661
9953
|
}
|
|
9662
9954
|
|
|
9663
9955
|
// src/commands/refresh.ts
|
|
9664
9956
|
import fs38 from "fs";
|
|
9665
|
-
import
|
|
9957
|
+
import path31 from "path";
|
|
9666
9958
|
import chalk19 from "chalk";
|
|
9667
9959
|
import ora6 from "ora";
|
|
9668
9960
|
|
|
@@ -9741,7 +10033,7 @@ function collectDiff(lastSha) {
|
|
|
9741
10033
|
|
|
9742
10034
|
// src/writers/refresh.ts
|
|
9743
10035
|
import fs35 from "fs";
|
|
9744
|
-
import
|
|
10036
|
+
import path28 from "path";
|
|
9745
10037
|
function writeRefreshDocs(docs) {
|
|
9746
10038
|
const written = [];
|
|
9747
10039
|
if (docs.claudeMd) {
|
|
@@ -9757,31 +10049,31 @@ function writeRefreshDocs(docs) {
|
|
|
9757
10049
|
written.push(".cursorrules");
|
|
9758
10050
|
}
|
|
9759
10051
|
if (docs.cursorRules) {
|
|
9760
|
-
const rulesDir =
|
|
10052
|
+
const rulesDir = path28.join(".cursor", "rules");
|
|
9761
10053
|
if (!fs35.existsSync(rulesDir)) fs35.mkdirSync(rulesDir, { recursive: true });
|
|
9762
10054
|
for (const rule of docs.cursorRules) {
|
|
9763
|
-
fs35.writeFileSync(
|
|
10055
|
+
fs35.writeFileSync(path28.join(rulesDir, rule.filename), rule.content);
|
|
9764
10056
|
written.push(`.cursor/rules/${rule.filename}`);
|
|
9765
10057
|
}
|
|
9766
10058
|
}
|
|
9767
10059
|
if (docs.claudeSkills) {
|
|
9768
|
-
const skillsDir =
|
|
10060
|
+
const skillsDir = path28.join(".claude", "skills");
|
|
9769
10061
|
if (!fs35.existsSync(skillsDir)) fs35.mkdirSync(skillsDir, { recursive: true });
|
|
9770
10062
|
for (const skill of docs.claudeSkills) {
|
|
9771
|
-
fs35.writeFileSync(
|
|
10063
|
+
fs35.writeFileSync(path28.join(skillsDir, skill.filename), skill.content);
|
|
9772
10064
|
written.push(`.claude/skills/${skill.filename}`);
|
|
9773
10065
|
}
|
|
9774
10066
|
}
|
|
9775
10067
|
if (docs.copilotInstructions) {
|
|
9776
10068
|
fs35.mkdirSync(".github", { recursive: true });
|
|
9777
|
-
fs35.writeFileSync(
|
|
10069
|
+
fs35.writeFileSync(path28.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(docs.copilotInstructions)));
|
|
9778
10070
|
written.push(".github/copilot-instructions.md");
|
|
9779
10071
|
}
|
|
9780
10072
|
if (docs.copilotInstructionFiles) {
|
|
9781
|
-
const instructionsDir =
|
|
10073
|
+
const instructionsDir = path28.join(".github", "instructions");
|
|
9782
10074
|
fs35.mkdirSync(instructionsDir, { recursive: true });
|
|
9783
10075
|
for (const file of docs.copilotInstructionFiles) {
|
|
9784
|
-
fs35.writeFileSync(
|
|
10076
|
+
fs35.writeFileSync(path28.join(instructionsDir, file.filename), file.content);
|
|
9785
10077
|
written.push(`.github/instructions/${file.filename}`);
|
|
9786
10078
|
}
|
|
9787
10079
|
}
|
|
@@ -9868,7 +10160,7 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
9868
10160
|
|
|
9869
10161
|
// src/learner/writer.ts
|
|
9870
10162
|
import fs36 from "fs";
|
|
9871
|
-
import
|
|
10163
|
+
import path29 from "path";
|
|
9872
10164
|
|
|
9873
10165
|
// src/learner/utils.ts
|
|
9874
10166
|
var TYPE_PREFIX_RE = /^\*\*\[[^\]]+\]\*\*\s*/;
|
|
@@ -9993,9 +10285,9 @@ function writeLearnedSection(content) {
|
|
|
9993
10285
|
return writeLearnedSectionTo(LEARNINGS_FILE, LEARNINGS_HEADER, readLearnedSection(), content);
|
|
9994
10286
|
}
|
|
9995
10287
|
function writeLearnedSkill(skill) {
|
|
9996
|
-
const skillDir =
|
|
10288
|
+
const skillDir = path29.join(".claude", "skills", skill.name);
|
|
9997
10289
|
if (!fs36.existsSync(skillDir)) fs36.mkdirSync(skillDir, { recursive: true });
|
|
9998
|
-
const skillPath =
|
|
10290
|
+
const skillPath = path29.join(skillDir, "SKILL.md");
|
|
9999
10291
|
if (!skill.isNew && fs36.existsSync(skillPath)) {
|
|
10000
10292
|
const existing = fs36.readFileSync(skillPath, "utf-8");
|
|
10001
10293
|
fs36.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
@@ -10072,8 +10364,8 @@ function discoverGitRepos(parentDir) {
|
|
|
10072
10364
|
const entries = fs38.readdirSync(parentDir, { withFileTypes: true });
|
|
10073
10365
|
for (const entry of entries) {
|
|
10074
10366
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
10075
|
-
const childPath =
|
|
10076
|
-
if (fs38.existsSync(
|
|
10367
|
+
const childPath = path31.join(parentDir, entry.name);
|
|
10368
|
+
if (fs38.existsSync(path31.join(childPath, ".git"))) {
|
|
10077
10369
|
repos.push(childPath);
|
|
10078
10370
|
}
|
|
10079
10371
|
}
|
|
@@ -10159,7 +10451,7 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
10159
10451
|
const filesToWrite = response.docsUpdated || [];
|
|
10160
10452
|
const preRefreshContents = /* @__PURE__ */ new Map();
|
|
10161
10453
|
for (const filePath of filesToWrite) {
|
|
10162
|
-
const fullPath =
|
|
10454
|
+
const fullPath = path31.resolve(repoDir, filePath);
|
|
10163
10455
|
try {
|
|
10164
10456
|
preRefreshContents.set(filePath, fs38.readFileSync(fullPath, "utf-8"));
|
|
10165
10457
|
} catch {
|
|
@@ -10171,7 +10463,7 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
10171
10463
|
const postScore = computeLocalScore(repoDir, targetAgent);
|
|
10172
10464
|
if (postScore.score < preScore.score) {
|
|
10173
10465
|
for (const [filePath, content] of preRefreshContents) {
|
|
10174
|
-
const fullPath =
|
|
10466
|
+
const fullPath = path31.resolve(repoDir, filePath);
|
|
10175
10467
|
if (content === null) {
|
|
10176
10468
|
try {
|
|
10177
10469
|
fs38.unlinkSync(fullPath);
|
|
@@ -10233,7 +10525,7 @@ async function refreshCommand(options) {
|
|
|
10233
10525
|
`));
|
|
10234
10526
|
const originalDir = process.cwd();
|
|
10235
10527
|
for (const repo of repos) {
|
|
10236
|
-
const repoName =
|
|
10528
|
+
const repoName = path31.basename(repo);
|
|
10237
10529
|
try {
|
|
10238
10530
|
process.chdir(repo);
|
|
10239
10531
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
@@ -10259,9 +10551,9 @@ import fs40 from "fs";
|
|
|
10259
10551
|
// src/lib/hooks.ts
|
|
10260
10552
|
init_resolve_caliber();
|
|
10261
10553
|
import fs39 from "fs";
|
|
10262
|
-
import
|
|
10554
|
+
import path32 from "path";
|
|
10263
10555
|
import { execSync as execSync15 } from "child_process";
|
|
10264
|
-
var SETTINGS_PATH2 =
|
|
10556
|
+
var SETTINGS_PATH2 = path32.join(".claude", "settings.json");
|
|
10265
10557
|
var REFRESH_TAIL = "refresh --quiet";
|
|
10266
10558
|
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
10267
10559
|
function getHookCommand() {
|
|
@@ -10276,7 +10568,7 @@ function readSettings2() {
|
|
|
10276
10568
|
}
|
|
10277
10569
|
}
|
|
10278
10570
|
function writeSettings2(settings) {
|
|
10279
|
-
const dir =
|
|
10571
|
+
const dir = path32.dirname(SETTINGS_PATH2);
|
|
10280
10572
|
if (!fs39.existsSync(dir)) fs39.mkdirSync(dir, { recursive: true });
|
|
10281
10573
|
fs39.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
|
|
10282
10574
|
}
|
|
@@ -10344,14 +10636,14 @@ ${PRECOMMIT_END}`;
|
|
|
10344
10636
|
function getGitHooksDir() {
|
|
10345
10637
|
try {
|
|
10346
10638
|
const gitDir = execSync15("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
10347
|
-
return
|
|
10639
|
+
return path32.join(gitDir, "hooks");
|
|
10348
10640
|
} catch {
|
|
10349
10641
|
return null;
|
|
10350
10642
|
}
|
|
10351
10643
|
}
|
|
10352
10644
|
function getPreCommitPath() {
|
|
10353
10645
|
const hooksDir = getGitHooksDir();
|
|
10354
|
-
return hooksDir ?
|
|
10646
|
+
return hooksDir ? path32.join(hooksDir, "pre-commit") : null;
|
|
10355
10647
|
}
|
|
10356
10648
|
function isPreCommitHookInstalled() {
|
|
10357
10649
|
const hookPath = getPreCommitPath();
|
|
@@ -10365,7 +10657,7 @@ function installPreCommitHook() {
|
|
|
10365
10657
|
}
|
|
10366
10658
|
const hookPath = getPreCommitPath();
|
|
10367
10659
|
if (!hookPath) return { installed: false, alreadyInstalled: false };
|
|
10368
|
-
const hooksDir =
|
|
10660
|
+
const hooksDir = path32.dirname(hookPath);
|
|
10369
10661
|
if (!fs39.existsSync(hooksDir)) fs39.mkdirSync(hooksDir, { recursive: true });
|
|
10370
10662
|
let content = "";
|
|
10371
10663
|
if (fs39.existsSync(hookPath)) {
|
|
@@ -10615,7 +10907,7 @@ async function configCommand() {
|
|
|
10615
10907
|
|
|
10616
10908
|
// src/commands/learn.ts
|
|
10617
10909
|
import fs44 from "fs";
|
|
10618
|
-
import
|
|
10910
|
+
import path36 from "path";
|
|
10619
10911
|
import chalk23 from "chalk";
|
|
10620
10912
|
|
|
10621
10913
|
// src/learner/stdin.ts
|
|
@@ -10647,7 +10939,7 @@ function readStdin() {
|
|
|
10647
10939
|
|
|
10648
10940
|
// src/learner/storage.ts
|
|
10649
10941
|
import fs41 from "fs";
|
|
10650
|
-
import
|
|
10942
|
+
import path33 from "path";
|
|
10651
10943
|
var MAX_RESPONSE_LENGTH = 2e3;
|
|
10652
10944
|
var DEFAULT_STATE = {
|
|
10653
10945
|
sessionId: null,
|
|
@@ -10661,10 +10953,10 @@ function ensureLearningDir() {
|
|
|
10661
10953
|
}
|
|
10662
10954
|
}
|
|
10663
10955
|
function sessionFilePath() {
|
|
10664
|
-
return
|
|
10956
|
+
return path33.join(getLearningDir(), LEARNING_SESSION_FILE);
|
|
10665
10957
|
}
|
|
10666
10958
|
function stateFilePath() {
|
|
10667
|
-
return
|
|
10959
|
+
return path33.join(getLearningDir(), LEARNING_STATE_FILE);
|
|
10668
10960
|
}
|
|
10669
10961
|
function truncateResponse(response) {
|
|
10670
10962
|
const str = JSON.stringify(response);
|
|
@@ -10736,7 +11028,7 @@ function resetState() {
|
|
|
10736
11028
|
var LOCK_FILE2 = "finalize.lock";
|
|
10737
11029
|
var LOCK_STALE_MS = 5 * 60 * 1e3;
|
|
10738
11030
|
function lockFilePath() {
|
|
10739
|
-
return
|
|
11031
|
+
return path33.join(getLearningDir(), LOCK_FILE2);
|
|
10740
11032
|
}
|
|
10741
11033
|
function acquireFinalizeLock() {
|
|
10742
11034
|
ensureLearningDir();
|
|
@@ -10782,10 +11074,10 @@ function releaseFinalizeLock() {
|
|
|
10782
11074
|
|
|
10783
11075
|
// src/lib/notifications.ts
|
|
10784
11076
|
import fs42 from "fs";
|
|
10785
|
-
import
|
|
11077
|
+
import path34 from "path";
|
|
10786
11078
|
import chalk22 from "chalk";
|
|
10787
11079
|
function notificationFilePath() {
|
|
10788
|
-
return
|
|
11080
|
+
return path34.join(getLearningDir(), "last-finalize-summary.json");
|
|
10789
11081
|
}
|
|
10790
11082
|
function writeFinalizeSummary(summary) {
|
|
10791
11083
|
try {
|
|
@@ -10967,7 +11259,7 @@ init_config();
|
|
|
10967
11259
|
|
|
10968
11260
|
// src/learner/roi.ts
|
|
10969
11261
|
import fs43 from "fs";
|
|
10970
|
-
import
|
|
11262
|
+
import path35 from "path";
|
|
10971
11263
|
var DEFAULT_TOTALS = {
|
|
10972
11264
|
totalWasteTokens: 0,
|
|
10973
11265
|
totalWasteSeconds: 0,
|
|
@@ -10981,7 +11273,7 @@ var DEFAULT_TOTALS = {
|
|
|
10981
11273
|
lastSessionTimestamp: ""
|
|
10982
11274
|
};
|
|
10983
11275
|
function roiFilePath() {
|
|
10984
|
-
return
|
|
11276
|
+
return path35.join(getLearningDir(), LEARNING_ROI_FILE);
|
|
10985
11277
|
}
|
|
10986
11278
|
function readROIStats() {
|
|
10987
11279
|
const filePath = roiFilePath();
|
|
@@ -11171,7 +11463,9 @@ Return a JSON object: {"matchedIndices": [0, 2]} or {"matchedIndices": []} if no
|
|
|
11171
11463
|
const json = extractJson(raw);
|
|
11172
11464
|
if (json) {
|
|
11173
11465
|
const parsed = JSON.parse(json);
|
|
11174
|
-
const indices = (parsed.matchedIndices || []).filter(
|
|
11466
|
+
const indices = (parsed.matchedIndices || []).filter(
|
|
11467
|
+
(i) => typeof i === "number" && i >= 0 && i < learnings.length
|
|
11468
|
+
);
|
|
11175
11469
|
return { matchedIndices: indices, unmatchedFailures: failureEvents.length - indices.length };
|
|
11176
11470
|
}
|
|
11177
11471
|
} catch {
|
|
@@ -11209,7 +11503,7 @@ var AUTO_SETTLE_MS = 200;
|
|
|
11209
11503
|
var INCREMENTAL_INTERVAL = 50;
|
|
11210
11504
|
function writeFinalizeError(message) {
|
|
11211
11505
|
try {
|
|
11212
|
-
const errorPath =
|
|
11506
|
+
const errorPath = path36.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
|
|
11213
11507
|
if (!fs44.existsSync(getLearningDir())) fs44.mkdirSync(getLearningDir(), { recursive: true });
|
|
11214
11508
|
fs44.writeFileSync(errorPath, JSON.stringify({
|
|
11215
11509
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -11221,7 +11515,7 @@ function writeFinalizeError(message) {
|
|
|
11221
11515
|
}
|
|
11222
11516
|
function readFinalizeError() {
|
|
11223
11517
|
try {
|
|
11224
|
-
const errorPath =
|
|
11518
|
+
const errorPath = path36.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
|
|
11225
11519
|
if (!fs44.existsSync(errorPath)) return null;
|
|
11226
11520
|
return JSON.parse(fs44.readFileSync(errorPath, "utf-8"));
|
|
11227
11521
|
} catch {
|
|
@@ -11270,7 +11564,7 @@ async function learnObserveCommand(options) {
|
|
|
11270
11564
|
const { resolveCaliber: resolveCaliber2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
|
|
11271
11565
|
const bin = resolveCaliber2();
|
|
11272
11566
|
const { spawn: spawn4 } = await import("child_process");
|
|
11273
|
-
const logPath =
|
|
11567
|
+
const logPath = path36.join(getLearningDir(), LEARNING_FINALIZE_LOG);
|
|
11274
11568
|
if (!fs44.existsSync(getLearningDir())) fs44.mkdirSync(getLearningDir(), { recursive: true });
|
|
11275
11569
|
const logFd = fs44.openSync(logPath, "a");
|
|
11276
11570
|
spawn4(bin, ["learn", "finalize", "--auto", "--incremental"], {
|
|
@@ -11560,7 +11854,7 @@ async function learnStatusCommand() {
|
|
|
11560
11854
|
if (lastError) {
|
|
11561
11855
|
console.log(`Last error: ${chalk23.red(lastError.error)}`);
|
|
11562
11856
|
console.log(chalk23.dim(` at ${lastError.timestamp}`));
|
|
11563
|
-
const logPath =
|
|
11857
|
+
const logPath = path36.join(getLearningDir(), LEARNING_FINALIZE_LOG);
|
|
11564
11858
|
if (fs44.existsSync(logPath)) {
|
|
11565
11859
|
console.log(chalk23.dim(` Full log: ${logPath}`));
|
|
11566
11860
|
}
|
|
@@ -11842,7 +12136,7 @@ async function insightsCommand(options) {
|
|
|
11842
12136
|
|
|
11843
12137
|
// src/commands/sources.ts
|
|
11844
12138
|
import fs45 from "fs";
|
|
11845
|
-
import
|
|
12139
|
+
import path37 from "path";
|
|
11846
12140
|
import chalk25 from "chalk";
|
|
11847
12141
|
init_resolve_caliber();
|
|
11848
12142
|
async function sourcesListCommand() {
|
|
@@ -11859,9 +12153,9 @@ async function sourcesListCommand() {
|
|
|
11859
12153
|
if (configSources.length > 0) {
|
|
11860
12154
|
for (const source of configSources) {
|
|
11861
12155
|
const sourcePath = source.path || source.url || "";
|
|
11862
|
-
const exists = source.path ? fs45.existsSync(
|
|
12156
|
+
const exists = source.path ? fs45.existsSync(path37.resolve(dir, source.path)) : false;
|
|
11863
12157
|
const status = exists ? chalk25.green("reachable") : chalk25.red("not found");
|
|
11864
|
-
const hasSummary = source.path && fs45.existsSync(
|
|
12158
|
+
const hasSummary = source.path && fs45.existsSync(path37.join(path37.resolve(dir, source.path), ".caliber", "summary.json"));
|
|
11865
12159
|
console.log(` ${chalk25.bold(source.role || source.type)} ${chalk25.dim(sourcePath)}`);
|
|
11866
12160
|
console.log(` Type: ${source.type} Status: ${status}${hasSummary ? " " + chalk25.cyan("has summary.json") : ""}`);
|
|
11867
12161
|
if (source.description) console.log(` ${chalk25.dim(source.description)}`);
|
|
@@ -11871,7 +12165,7 @@ async function sourcesListCommand() {
|
|
|
11871
12165
|
if (workspaces.length > 0) {
|
|
11872
12166
|
console.log(chalk25.dim(" Auto-detected workspaces:"));
|
|
11873
12167
|
for (const ws of workspaces) {
|
|
11874
|
-
const exists = fs45.existsSync(
|
|
12168
|
+
const exists = fs45.existsSync(path37.resolve(dir, ws));
|
|
11875
12169
|
console.log(` ${exists ? chalk25.green("\u25CF") : chalk25.red("\u25CF")} ${ws}`);
|
|
11876
12170
|
}
|
|
11877
12171
|
console.log("");
|
|
@@ -11879,7 +12173,7 @@ async function sourcesListCommand() {
|
|
|
11879
12173
|
}
|
|
11880
12174
|
async function sourcesAddCommand(sourcePath) {
|
|
11881
12175
|
const dir = process.cwd();
|
|
11882
|
-
const absPath =
|
|
12176
|
+
const absPath = path37.resolve(dir, sourcePath);
|
|
11883
12177
|
if (!fs45.existsSync(absPath)) {
|
|
11884
12178
|
console.log(chalk25.red(`
|
|
11885
12179
|
Path not found: ${sourcePath}
|
|
@@ -11895,7 +12189,7 @@ async function sourcesAddCommand(sourcePath) {
|
|
|
11895
12189
|
}
|
|
11896
12190
|
const existing = loadSourcesConfig(dir);
|
|
11897
12191
|
const alreadyConfigured = existing.some(
|
|
11898
|
-
(s) => s.path &&
|
|
12192
|
+
(s) => s.path && path37.resolve(dir, s.path) === absPath
|
|
11899
12193
|
);
|
|
11900
12194
|
if (alreadyConfigured) {
|
|
11901
12195
|
console.log(chalk25.yellow(`
|
|
@@ -11944,7 +12238,7 @@ async function sourcesRemoveCommand(name) {
|
|
|
11944
12238
|
|
|
11945
12239
|
// src/commands/publish.ts
|
|
11946
12240
|
import fs46 from "fs";
|
|
11947
|
-
import
|
|
12241
|
+
import path38 from "path";
|
|
11948
12242
|
import chalk26 from "chalk";
|
|
11949
12243
|
import ora7 from "ora";
|
|
11950
12244
|
init_config();
|
|
@@ -11959,10 +12253,10 @@ async function publishCommand() {
|
|
|
11959
12253
|
const spinner = ora7("Generating project summary...").start();
|
|
11960
12254
|
try {
|
|
11961
12255
|
const fingerprint = await collectFingerprint(dir);
|
|
11962
|
-
const claudeMd = readFileOrNull(
|
|
12256
|
+
const claudeMd = readFileOrNull(path38.join(dir, "CLAUDE.md"));
|
|
11963
12257
|
const topLevelDirs = fingerprint.fileTree.filter((f) => f.endsWith("/") && !f.includes("/")).map((f) => f.replace(/\/$/, ""));
|
|
11964
12258
|
const summary = {
|
|
11965
|
-
name: fingerprint.packageName ||
|
|
12259
|
+
name: fingerprint.packageName || path38.basename(dir),
|
|
11966
12260
|
version: "1.0.0",
|
|
11967
12261
|
description: fingerprint.description || "",
|
|
11968
12262
|
languages: fingerprint.languages,
|
|
@@ -11974,7 +12268,7 @@ async function publishCommand() {
|
|
|
11974
12268
|
summary.conventions = claudeMd.slice(0, 2e3);
|
|
11975
12269
|
}
|
|
11976
12270
|
try {
|
|
11977
|
-
const pkgContent = readFileOrNull(
|
|
12271
|
+
const pkgContent = readFileOrNull(path38.join(dir, "package.json"));
|
|
11978
12272
|
if (pkgContent) {
|
|
11979
12273
|
const pkg3 = JSON.parse(pkgContent);
|
|
11980
12274
|
if (pkg3.scripts) {
|
|
@@ -11987,14 +12281,14 @@ async function publishCommand() {
|
|
|
11987
12281
|
}
|
|
11988
12282
|
} catch {
|
|
11989
12283
|
}
|
|
11990
|
-
const outputDir =
|
|
12284
|
+
const outputDir = path38.join(dir, ".caliber");
|
|
11991
12285
|
if (!fs46.existsSync(outputDir)) {
|
|
11992
12286
|
fs46.mkdirSync(outputDir, { recursive: true });
|
|
11993
12287
|
}
|
|
11994
|
-
const outputPath =
|
|
12288
|
+
const outputPath = path38.join(outputDir, "summary.json");
|
|
11995
12289
|
fs46.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + "\n", "utf-8");
|
|
11996
12290
|
spinner.succeed("Project summary published");
|
|
11997
|
-
console.log(` ${chalk26.green("\u2713")} ${
|
|
12291
|
+
console.log(` ${chalk26.green("\u2713")} ${path38.relative(dir, outputPath)}`);
|
|
11998
12292
|
console.log(chalk26.dim("\n Other projects can now reference this repo as a source."));
|
|
11999
12293
|
console.log(chalk26.dim(" When they run `caliber init`, they'll read this summary automatically.\n"));
|
|
12000
12294
|
} catch (err) {
|
|
@@ -12006,9 +12300,9 @@ async function publishCommand() {
|
|
|
12006
12300
|
}
|
|
12007
12301
|
|
|
12008
12302
|
// src/cli.ts
|
|
12009
|
-
var __dirname =
|
|
12303
|
+
var __dirname = path39.dirname(fileURLToPath(import.meta.url));
|
|
12010
12304
|
var pkg = JSON.parse(
|
|
12011
|
-
fs47.readFileSync(
|
|
12305
|
+
fs47.readFileSync(path39.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
12012
12306
|
);
|
|
12013
12307
|
var program = new Command();
|
|
12014
12308
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
@@ -12096,16 +12390,14 @@ learn.command("add <content>").description("Add a learning directly (used by age
|
|
|
12096
12390
|
|
|
12097
12391
|
// src/utils/version-check.ts
|
|
12098
12392
|
import fs48 from "fs";
|
|
12099
|
-
import
|
|
12393
|
+
import path40 from "path";
|
|
12100
12394
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12101
12395
|
import { execSync as execSync16, execFileSync as execFileSync3 } from "child_process";
|
|
12102
12396
|
import chalk27 from "chalk";
|
|
12103
12397
|
import ora8 from "ora";
|
|
12104
12398
|
import confirm2 from "@inquirer/confirm";
|
|
12105
|
-
var __dirname_vc =
|
|
12106
|
-
var pkg2 = JSON.parse(
|
|
12107
|
-
fs48.readFileSync(path39.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
12108
|
-
);
|
|
12399
|
+
var __dirname_vc = path40.dirname(fileURLToPath2(import.meta.url));
|
|
12400
|
+
var pkg2 = JSON.parse(fs48.readFileSync(path40.resolve(__dirname_vc, "..", "package.json"), "utf-8"));
|
|
12109
12401
|
function getChannel(version) {
|
|
12110
12402
|
const match = version.match(/-(dev|next)\./);
|
|
12111
12403
|
return match ? match[1] : "latest";
|
|
@@ -12128,8 +12420,11 @@ function isNewer(registry, current) {
|
|
|
12128
12420
|
}
|
|
12129
12421
|
function getInstalledVersion() {
|
|
12130
12422
|
try {
|
|
12131
|
-
const globalRoot = execSync16("npm root -g", {
|
|
12132
|
-
|
|
12423
|
+
const globalRoot = execSync16("npm root -g", {
|
|
12424
|
+
encoding: "utf-8",
|
|
12425
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
12426
|
+
}).trim();
|
|
12427
|
+
const pkgPath = path40.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
12133
12428
|
return JSON.parse(fs48.readFileSync(pkgPath, "utf-8")).version;
|
|
12134
12429
|
} catch {
|
|
12135
12430
|
return null;
|
|
@@ -12164,17 +12459,18 @@ Run ${chalk27.bold(`npm install -g @rely-ai/caliber${installTag}`)} to upgrade.
|
|
|
12164
12459
|
);
|
|
12165
12460
|
return;
|
|
12166
12461
|
}
|
|
12167
|
-
console.log(
|
|
12168
|
-
|
|
12169
|
-
|
|
12170
|
-
|
|
12171
|
-
|
|
12462
|
+
console.log(chalk27.yellow(`
|
|
12463
|
+
Update available: ${current} -> ${latest}`));
|
|
12464
|
+
const shouldUpdate = await confirm2({
|
|
12465
|
+
message: "Would you like to update now? (Y/n)",
|
|
12466
|
+
default: true
|
|
12467
|
+
});
|
|
12172
12468
|
if (!shouldUpdate) {
|
|
12173
12469
|
console.log();
|
|
12174
12470
|
return;
|
|
12175
12471
|
}
|
|
12176
12472
|
const tag = channel === "latest" ? latest : channel;
|
|
12177
|
-
if (!/^[\w
|
|
12473
|
+
if (!/^[\w.-]+$/.test(tag)) return;
|
|
12178
12474
|
const spinner = ora8("Updating caliber...").start();
|
|
12179
12475
|
try {
|
|
12180
12476
|
execFileSync3("npm", ["install", "-g", `@rely-ai/caliber@${tag}`], {
|
|
@@ -12185,8 +12481,10 @@ Update available: ${current} -> ${latest}`)
|
|
|
12185
12481
|
const installed = getInstalledVersion();
|
|
12186
12482
|
if (installed !== latest) {
|
|
12187
12483
|
spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
|
|
12188
|
-
console.log(
|
|
12189
|
-
`)
|
|
12484
|
+
console.log(
|
|
12485
|
+
chalk27.yellow(`Run ${chalk27.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually.
|
|
12486
|
+
`)
|
|
12487
|
+
);
|
|
12190
12488
|
return;
|
|
12191
12489
|
}
|
|
12192
12490
|
spinner.succeed(chalk27.green(`Updated to ${latest}`));
|