@rely-ai/caliber 1.26.1 → 1.27.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 +343 -321
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -180,7 +180,7 @@ __export(resolve_caliber_exports, {
|
|
|
180
180
|
isCaliberCommand: () => isCaliberCommand,
|
|
181
181
|
resolveCaliber: () => resolveCaliber
|
|
182
182
|
});
|
|
183
|
-
import
|
|
183
|
+
import fs20 from "fs";
|
|
184
184
|
import { execSync as execSync6 } from "child_process";
|
|
185
185
|
function resolveCaliber() {
|
|
186
186
|
if (_resolved) return _resolved;
|
|
@@ -202,7 +202,7 @@ function resolveCaliber() {
|
|
|
202
202
|
} catch {
|
|
203
203
|
}
|
|
204
204
|
const binPath = process.argv[1];
|
|
205
|
-
if (binPath &&
|
|
205
|
+
if (binPath && fs20.existsSync(binPath)) {
|
|
206
206
|
_resolved = binPath;
|
|
207
207
|
return _resolved;
|
|
208
208
|
}
|
|
@@ -228,7 +228,7 @@ var init_resolve_caliber = __esm({
|
|
|
228
228
|
import { execSync as execSync12, spawn as spawn3 } from "child_process";
|
|
229
229
|
import fs26 from "fs";
|
|
230
230
|
import path22 from "path";
|
|
231
|
-
import
|
|
231
|
+
import os6 from "os";
|
|
232
232
|
function getEmptyFilePath(proposedPath) {
|
|
233
233
|
fs26.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
|
|
234
234
|
const tempPath = path22.join(DIFF_TEMP_DIR, path22.basename(proposedPath));
|
|
@@ -272,7 +272,7 @@ var init_editor = __esm({
|
|
|
272
272
|
"src/utils/editor.ts"() {
|
|
273
273
|
"use strict";
|
|
274
274
|
IS_WINDOWS3 = process.platform === "win32";
|
|
275
|
-
DIFF_TEMP_DIR = path22.join(
|
|
275
|
+
DIFF_TEMP_DIR = path22.join(os6.tmpdir(), "caliber-diff");
|
|
276
276
|
}
|
|
277
277
|
});
|
|
278
278
|
|
|
@@ -499,7 +499,7 @@ __export(lock_exports, {
|
|
|
499
499
|
});
|
|
500
500
|
import fs36 from "fs";
|
|
501
501
|
import path28 from "path";
|
|
502
|
-
import
|
|
502
|
+
import os8 from "os";
|
|
503
503
|
function isCaliberRunning() {
|
|
504
504
|
try {
|
|
505
505
|
if (!fs36.existsSync(LOCK_FILE)) return false;
|
|
@@ -532,7 +532,7 @@ var LOCK_FILE, STALE_MS;
|
|
|
532
532
|
var init_lock = __esm({
|
|
533
533
|
"src/lib/lock.ts"() {
|
|
534
534
|
"use strict";
|
|
535
|
-
LOCK_FILE = path28.join(
|
|
535
|
+
LOCK_FILE = path28.join(os8.tmpdir(), ".caliber.lock");
|
|
536
536
|
STALE_MS = 10 * 60 * 1e3;
|
|
537
537
|
}
|
|
538
538
|
});
|
|
@@ -2913,9 +2913,180 @@ async function enrichWithLLM(fingerprint) {
|
|
|
2913
2913
|
}
|
|
2914
2914
|
}
|
|
2915
2915
|
|
|
2916
|
-
// src/
|
|
2916
|
+
// src/scanner/index.ts
|
|
2917
2917
|
import fs8 from "fs";
|
|
2918
2918
|
import path8 from "path";
|
|
2919
|
+
import crypto2 from "crypto";
|
|
2920
|
+
import os4 from "os";
|
|
2921
|
+
function detectPlatforms() {
|
|
2922
|
+
const home = os4.homedir();
|
|
2923
|
+
return {
|
|
2924
|
+
claude: fs8.existsSync(path8.join(home, ".claude")),
|
|
2925
|
+
cursor: fs8.existsSync(getCursorConfigDir()),
|
|
2926
|
+
codex: fs8.existsSync(path8.join(home, ".codex"))
|
|
2927
|
+
};
|
|
2928
|
+
}
|
|
2929
|
+
function scanLocalState(dir) {
|
|
2930
|
+
const items = [];
|
|
2931
|
+
const claudeMdPath = path8.join(dir, "CLAUDE.md");
|
|
2932
|
+
if (fs8.existsSync(claudeMdPath)) {
|
|
2933
|
+
items.push({
|
|
2934
|
+
type: "rule",
|
|
2935
|
+
platform: "claude",
|
|
2936
|
+
name: "CLAUDE.md",
|
|
2937
|
+
contentHash: hashFile(claudeMdPath),
|
|
2938
|
+
path: claudeMdPath
|
|
2939
|
+
});
|
|
2940
|
+
}
|
|
2941
|
+
const skillsDir = path8.join(dir, ".claude", "skills");
|
|
2942
|
+
if (fs8.existsSync(skillsDir)) {
|
|
2943
|
+
for (const file of fs8.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
2944
|
+
const filePath = path8.join(skillsDir, file);
|
|
2945
|
+
items.push({
|
|
2946
|
+
type: "skill",
|
|
2947
|
+
platform: "claude",
|
|
2948
|
+
name: file,
|
|
2949
|
+
contentHash: hashFile(filePath),
|
|
2950
|
+
path: filePath
|
|
2951
|
+
});
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
const mcpJsonPath = path8.join(dir, ".mcp.json");
|
|
2955
|
+
if (fs8.existsSync(mcpJsonPath)) {
|
|
2956
|
+
try {
|
|
2957
|
+
const mcpJson = JSON.parse(fs8.readFileSync(mcpJsonPath, "utf-8"));
|
|
2958
|
+
if (mcpJson.mcpServers) {
|
|
2959
|
+
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
2960
|
+
items.push({
|
|
2961
|
+
type: "mcp",
|
|
2962
|
+
platform: "claude",
|
|
2963
|
+
name,
|
|
2964
|
+
contentHash: hashJson(mcpJson.mcpServers[name]),
|
|
2965
|
+
path: mcpJsonPath
|
|
2966
|
+
});
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
} catch (error) {
|
|
2970
|
+
warnScanSkip(".mcp.json", error);
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
const agentsMdPath = path8.join(dir, "AGENTS.md");
|
|
2974
|
+
if (fs8.existsSync(agentsMdPath)) {
|
|
2975
|
+
items.push({
|
|
2976
|
+
type: "rule",
|
|
2977
|
+
platform: "codex",
|
|
2978
|
+
name: "AGENTS.md",
|
|
2979
|
+
contentHash: hashFile(agentsMdPath),
|
|
2980
|
+
path: agentsMdPath
|
|
2981
|
+
});
|
|
2982
|
+
}
|
|
2983
|
+
const codexSkillsDir = path8.join(dir, ".agents", "skills");
|
|
2984
|
+
if (fs8.existsSync(codexSkillsDir)) {
|
|
2985
|
+
try {
|
|
2986
|
+
for (const name of fs8.readdirSync(codexSkillsDir)) {
|
|
2987
|
+
const skillFile = path8.join(codexSkillsDir, name, "SKILL.md");
|
|
2988
|
+
if (fs8.existsSync(skillFile)) {
|
|
2989
|
+
items.push({
|
|
2990
|
+
type: "skill",
|
|
2991
|
+
platform: "codex",
|
|
2992
|
+
name: `${name}/SKILL.md`,
|
|
2993
|
+
contentHash: hashFile(skillFile),
|
|
2994
|
+
path: skillFile
|
|
2995
|
+
});
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
} catch (error) {
|
|
2999
|
+
warnScanSkip(".agents/skills", error);
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
const cursorrulesPath = path8.join(dir, ".cursorrules");
|
|
3003
|
+
if (fs8.existsSync(cursorrulesPath)) {
|
|
3004
|
+
items.push({
|
|
3005
|
+
type: "rule",
|
|
3006
|
+
platform: "cursor",
|
|
3007
|
+
name: ".cursorrules",
|
|
3008
|
+
contentHash: hashFile(cursorrulesPath),
|
|
3009
|
+
path: cursorrulesPath
|
|
3010
|
+
});
|
|
3011
|
+
}
|
|
3012
|
+
const cursorRulesDir = path8.join(dir, ".cursor", "rules");
|
|
3013
|
+
if (fs8.existsSync(cursorRulesDir)) {
|
|
3014
|
+
for (const file of fs8.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
3015
|
+
const filePath = path8.join(cursorRulesDir, file);
|
|
3016
|
+
items.push({
|
|
3017
|
+
type: "rule",
|
|
3018
|
+
platform: "cursor",
|
|
3019
|
+
name: file,
|
|
3020
|
+
contentHash: hashFile(filePath),
|
|
3021
|
+
path: filePath
|
|
3022
|
+
});
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
const cursorSkillsDir = path8.join(dir, ".cursor", "skills");
|
|
3026
|
+
if (fs8.existsSync(cursorSkillsDir)) {
|
|
3027
|
+
try {
|
|
3028
|
+
for (const name of fs8.readdirSync(cursorSkillsDir)) {
|
|
3029
|
+
const skillFile = path8.join(cursorSkillsDir, name, "SKILL.md");
|
|
3030
|
+
if (fs8.existsSync(skillFile)) {
|
|
3031
|
+
items.push({
|
|
3032
|
+
type: "skill",
|
|
3033
|
+
platform: "cursor",
|
|
3034
|
+
name: `${name}/SKILL.md`,
|
|
3035
|
+
contentHash: hashFile(skillFile),
|
|
3036
|
+
path: skillFile
|
|
3037
|
+
});
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
} catch (error) {
|
|
3041
|
+
warnScanSkip(".cursor/skills", error);
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
const cursorMcpPath = path8.join(dir, ".cursor", "mcp.json");
|
|
3045
|
+
if (fs8.existsSync(cursorMcpPath)) {
|
|
3046
|
+
try {
|
|
3047
|
+
const mcpJson = JSON.parse(fs8.readFileSync(cursorMcpPath, "utf-8"));
|
|
3048
|
+
if (mcpJson.mcpServers) {
|
|
3049
|
+
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
3050
|
+
items.push({
|
|
3051
|
+
type: "mcp",
|
|
3052
|
+
platform: "cursor",
|
|
3053
|
+
name,
|
|
3054
|
+
contentHash: hashJson(mcpJson.mcpServers[name]),
|
|
3055
|
+
path: cursorMcpPath
|
|
3056
|
+
});
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
} catch (error) {
|
|
3060
|
+
warnScanSkip(".cursor/mcp.json", error);
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
return items;
|
|
3064
|
+
}
|
|
3065
|
+
function hashFile(filePath) {
|
|
3066
|
+
const text = fs8.readFileSync(filePath, "utf-8");
|
|
3067
|
+
return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
3068
|
+
}
|
|
3069
|
+
function hashJson(obj) {
|
|
3070
|
+
return crypto2.createHash("sha256").update(JSON.stringify(obj)).digest("hex");
|
|
3071
|
+
}
|
|
3072
|
+
function warnScanSkip(target, error) {
|
|
3073
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3074
|
+
console.warn(`Warning: ${target} scan skipped (${message})`);
|
|
3075
|
+
}
|
|
3076
|
+
function getCursorConfigDir() {
|
|
3077
|
+
const home = os4.homedir();
|
|
3078
|
+
if (process.platform === "darwin") {
|
|
3079
|
+
return path8.join(home, "Library", "Application Support", "Cursor");
|
|
3080
|
+
}
|
|
3081
|
+
if (process.platform === "win32") {
|
|
3082
|
+
return path8.join(home, "AppData", "Roaming", "Cursor");
|
|
3083
|
+
}
|
|
3084
|
+
return path8.join(home, ".config", "Cursor");
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
// src/fingerprint/sources.ts
|
|
3088
|
+
import fs9 from "fs";
|
|
3089
|
+
import path9 from "path";
|
|
2919
3090
|
|
|
2920
3091
|
// src/scoring/utils.ts
|
|
2921
3092
|
import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
|
|
@@ -3194,7 +3365,7 @@ var SOURCE_CONTENT_LIMIT = 2e3;
|
|
|
3194
3365
|
var README_CONTENT_LIMIT = 1e3;
|
|
3195
3366
|
var ORIGIN_PRIORITY = { cli: 0, config: 1, workspace: 2 };
|
|
3196
3367
|
function loadSourcesConfig(dir) {
|
|
3197
|
-
const configPath =
|
|
3368
|
+
const configPath = path9.join(dir, ".caliber", "sources.json");
|
|
3198
3369
|
const content = readFileOrNull(configPath);
|
|
3199
3370
|
if (!content) return [];
|
|
3200
3371
|
try {
|
|
@@ -3212,29 +3383,29 @@ function loadSourcesConfig(dir) {
|
|
|
3212
3383
|
}
|
|
3213
3384
|
}
|
|
3214
3385
|
function writeSourcesConfig(dir, sources2) {
|
|
3215
|
-
const configDir =
|
|
3216
|
-
if (!
|
|
3217
|
-
|
|
3386
|
+
const configDir = path9.join(dir, ".caliber");
|
|
3387
|
+
if (!fs9.existsSync(configDir)) {
|
|
3388
|
+
fs9.mkdirSync(configDir, { recursive: true });
|
|
3218
3389
|
}
|
|
3219
|
-
const configPath =
|
|
3220
|
-
|
|
3390
|
+
const configPath = path9.join(configDir, "sources.json");
|
|
3391
|
+
fs9.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
|
|
3221
3392
|
}
|
|
3222
3393
|
function detectSourceType(absPath) {
|
|
3223
3394
|
try {
|
|
3224
|
-
return
|
|
3395
|
+
return fs9.statSync(absPath).isDirectory() ? "repo" : "file";
|
|
3225
3396
|
} catch {
|
|
3226
3397
|
return "file";
|
|
3227
3398
|
}
|
|
3228
3399
|
}
|
|
3229
3400
|
function isInsideDir(childPath, parentDir) {
|
|
3230
|
-
const relative2 =
|
|
3231
|
-
return !relative2.startsWith("..") && !
|
|
3401
|
+
const relative2 = path9.relative(parentDir, childPath);
|
|
3402
|
+
return !relative2.startsWith("..") && !path9.isAbsolute(relative2);
|
|
3232
3403
|
}
|
|
3233
3404
|
function resolveAllSources(dir, cliSources, workspaces) {
|
|
3234
3405
|
const seen = /* @__PURE__ */ new Map();
|
|
3235
|
-
const projectRoot =
|
|
3406
|
+
const projectRoot = path9.resolve(dir);
|
|
3236
3407
|
for (const src of cliSources) {
|
|
3237
|
-
const absPath =
|
|
3408
|
+
const absPath = path9.resolve(dir, src);
|
|
3238
3409
|
if (seen.has(absPath)) continue;
|
|
3239
3410
|
const type = detectSourceType(absPath);
|
|
3240
3411
|
seen.set(absPath, {
|
|
@@ -3247,12 +3418,12 @@ function resolveAllSources(dir, cliSources, workspaces) {
|
|
|
3247
3418
|
for (const cfg of configSources) {
|
|
3248
3419
|
if (cfg.type === "url") continue;
|
|
3249
3420
|
if (!cfg.path) continue;
|
|
3250
|
-
const absPath =
|
|
3421
|
+
const absPath = path9.resolve(dir, cfg.path);
|
|
3251
3422
|
if (seen.has(absPath)) continue;
|
|
3252
3423
|
seen.set(absPath, { absPath, config: cfg, origin: "config" });
|
|
3253
3424
|
}
|
|
3254
3425
|
for (const ws of workspaces) {
|
|
3255
|
-
const absPath =
|
|
3426
|
+
const absPath = path9.resolve(dir, ws);
|
|
3256
3427
|
if (seen.has(absPath)) continue;
|
|
3257
3428
|
if (!isInsideDir(absPath, projectRoot)) continue;
|
|
3258
3429
|
seen.set(absPath, {
|
|
@@ -3265,7 +3436,7 @@ function resolveAllSources(dir, cliSources, workspaces) {
|
|
|
3265
3436
|
for (const [absPath, resolved] of seen) {
|
|
3266
3437
|
let stat;
|
|
3267
3438
|
try {
|
|
3268
|
-
stat =
|
|
3439
|
+
stat = fs9.statSync(absPath);
|
|
3269
3440
|
} catch {
|
|
3270
3441
|
console.warn(`Source ${resolved.config.path || absPath} not found, skipping`);
|
|
3271
3442
|
continue;
|
|
@@ -3294,13 +3465,13 @@ function collectSourceSummary(resolved, projectDir) {
|
|
|
3294
3465
|
if (config.type === "file") {
|
|
3295
3466
|
return collectFileSummary(resolved, projectDir);
|
|
3296
3467
|
}
|
|
3297
|
-
const summaryPath =
|
|
3468
|
+
const summaryPath = path9.join(absPath, ".caliber", "summary.json");
|
|
3298
3469
|
const summaryContent = readFileOrNull(summaryPath);
|
|
3299
3470
|
if (summaryContent) {
|
|
3300
3471
|
try {
|
|
3301
3472
|
const published = JSON.parse(summaryContent);
|
|
3302
3473
|
return {
|
|
3303
|
-
name: published.name ||
|
|
3474
|
+
name: published.name || path9.basename(absPath),
|
|
3304
3475
|
type: "repo",
|
|
3305
3476
|
role: config.role || published.role || "related-repo",
|
|
3306
3477
|
description: config.description || published.description || "",
|
|
@@ -3320,18 +3491,18 @@ function collectRepoSummary(resolved, projectDir) {
|
|
|
3320
3491
|
let topLevelDirs;
|
|
3321
3492
|
let keyFiles;
|
|
3322
3493
|
try {
|
|
3323
|
-
const entries =
|
|
3494
|
+
const entries = fs9.readdirSync(absPath, { withFileTypes: true });
|
|
3324
3495
|
topLevelDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name).slice(0, 20);
|
|
3325
3496
|
keyFiles = entries.filter((e) => e.isFile() && !e.name.startsWith(".")).map((e) => e.name).slice(0, 15);
|
|
3326
3497
|
} catch {
|
|
3327
3498
|
}
|
|
3328
|
-
const claudeMdContent = readFileOrNull(
|
|
3499
|
+
const claudeMdContent = readFileOrNull(path9.join(absPath, "CLAUDE.md"));
|
|
3329
3500
|
const existingClaudeMd = claudeMdContent ? claudeMdContent.slice(0, SOURCE_CONTENT_LIMIT) : void 0;
|
|
3330
|
-
const readmeContent = readFileOrNull(
|
|
3501
|
+
const readmeContent = readFileOrNull(path9.join(absPath, "README.md"));
|
|
3331
3502
|
const readmeExcerpt = readmeContent ? readmeContent.slice(0, README_CONTENT_LIMIT) : void 0;
|
|
3332
3503
|
const gitRemoteUrl = getGitRemoteUrl(absPath);
|
|
3333
3504
|
return {
|
|
3334
|
-
name: packageName ||
|
|
3505
|
+
name: packageName || path9.basename(absPath),
|
|
3335
3506
|
type: "repo",
|
|
3336
3507
|
role: config.role || "related-repo",
|
|
3337
3508
|
description: config.description || "",
|
|
@@ -3348,7 +3519,7 @@ function collectFileSummary(resolved, projectDir) {
|
|
|
3348
3519
|
const { config, origin, absPath } = resolved;
|
|
3349
3520
|
const content = readFileOrNull(absPath);
|
|
3350
3521
|
return {
|
|
3351
|
-
name:
|
|
3522
|
+
name: path9.basename(absPath),
|
|
3352
3523
|
type: "file",
|
|
3353
3524
|
role: config.role || "reference-doc",
|
|
3354
3525
|
description: config.description || content?.slice(0, 100).split("\n")[0] || "",
|
|
@@ -3987,11 +4158,11 @@ ${f.content}
|
|
|
3987
4158
|
}
|
|
3988
4159
|
|
|
3989
4160
|
// src/writers/index.ts
|
|
3990
|
-
import
|
|
4161
|
+
import fs17 from "fs";
|
|
3991
4162
|
|
|
3992
4163
|
// src/writers/claude/index.ts
|
|
3993
|
-
import
|
|
3994
|
-
import
|
|
4164
|
+
import fs10 from "fs";
|
|
4165
|
+
import path10 from "path";
|
|
3995
4166
|
|
|
3996
4167
|
// src/writers/pre-commit-block.ts
|
|
3997
4168
|
var BLOCK_START = "<!-- caliber:managed:pre-commit -->";
|
|
@@ -4057,13 +4228,13 @@ function getCursorLearningsRule() {
|
|
|
4057
4228
|
// src/writers/claude/index.ts
|
|
4058
4229
|
function writeClaudeConfig(config) {
|
|
4059
4230
|
const written = [];
|
|
4060
|
-
|
|
4231
|
+
fs10.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(config.claudeMd)));
|
|
4061
4232
|
written.push("CLAUDE.md");
|
|
4062
4233
|
if (config.skills?.length) {
|
|
4063
4234
|
for (const skill of config.skills) {
|
|
4064
|
-
const skillDir =
|
|
4065
|
-
if (!
|
|
4066
|
-
const skillPath =
|
|
4235
|
+
const skillDir = path10.join(".claude", "skills", skill.name);
|
|
4236
|
+
if (!fs10.existsSync(skillDir)) fs10.mkdirSync(skillDir, { recursive: true });
|
|
4237
|
+
const skillPath = path10.join(skillDir, "SKILL.md");
|
|
4067
4238
|
const frontmatter = [
|
|
4068
4239
|
"---",
|
|
4069
4240
|
`name: ${skill.name}`,
|
|
@@ -4071,50 +4242,50 @@ function writeClaudeConfig(config) {
|
|
|
4071
4242
|
"---",
|
|
4072
4243
|
""
|
|
4073
4244
|
].join("\n");
|
|
4074
|
-
|
|
4245
|
+
fs10.writeFileSync(skillPath, frontmatter + skill.content);
|
|
4075
4246
|
written.push(skillPath);
|
|
4076
4247
|
}
|
|
4077
4248
|
}
|
|
4078
4249
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
4079
4250
|
let existingServers = {};
|
|
4080
4251
|
try {
|
|
4081
|
-
if (
|
|
4082
|
-
const existing = JSON.parse(
|
|
4252
|
+
if (fs10.existsSync(".mcp.json")) {
|
|
4253
|
+
const existing = JSON.parse(fs10.readFileSync(".mcp.json", "utf-8"));
|
|
4083
4254
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
4084
4255
|
}
|
|
4085
4256
|
} catch {
|
|
4086
4257
|
}
|
|
4087
4258
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
4088
|
-
|
|
4259
|
+
fs10.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
4089
4260
|
written.push(".mcp.json");
|
|
4090
4261
|
}
|
|
4091
4262
|
return written;
|
|
4092
4263
|
}
|
|
4093
4264
|
|
|
4094
4265
|
// src/writers/cursor/index.ts
|
|
4095
|
-
import
|
|
4096
|
-
import
|
|
4266
|
+
import fs11 from "fs";
|
|
4267
|
+
import path11 from "path";
|
|
4097
4268
|
function writeCursorConfig(config) {
|
|
4098
4269
|
const written = [];
|
|
4099
4270
|
if (config.cursorrules) {
|
|
4100
|
-
|
|
4271
|
+
fs11.writeFileSync(".cursorrules", config.cursorrules);
|
|
4101
4272
|
written.push(".cursorrules");
|
|
4102
4273
|
}
|
|
4103
4274
|
const preCommitRule = getCursorPreCommitRule();
|
|
4104
4275
|
const learningsRule = getCursorLearningsRule();
|
|
4105
4276
|
const allRules = [...config.rules || [], preCommitRule, learningsRule];
|
|
4106
|
-
const rulesDir =
|
|
4107
|
-
if (!
|
|
4277
|
+
const rulesDir = path11.join(".cursor", "rules");
|
|
4278
|
+
if (!fs11.existsSync(rulesDir)) fs11.mkdirSync(rulesDir, { recursive: true });
|
|
4108
4279
|
for (const rule of allRules) {
|
|
4109
|
-
const rulePath =
|
|
4110
|
-
|
|
4280
|
+
const rulePath = path11.join(rulesDir, rule.filename);
|
|
4281
|
+
fs11.writeFileSync(rulePath, rule.content);
|
|
4111
4282
|
written.push(rulePath);
|
|
4112
4283
|
}
|
|
4113
4284
|
if (config.skills?.length) {
|
|
4114
4285
|
for (const skill of config.skills) {
|
|
4115
|
-
const skillDir =
|
|
4116
|
-
if (!
|
|
4117
|
-
const skillPath =
|
|
4286
|
+
const skillDir = path11.join(".cursor", "skills", skill.name);
|
|
4287
|
+
if (!fs11.existsSync(skillDir)) fs11.mkdirSync(skillDir, { recursive: true });
|
|
4288
|
+
const skillPath = path11.join(skillDir, "SKILL.md");
|
|
4118
4289
|
const frontmatter = [
|
|
4119
4290
|
"---",
|
|
4120
4291
|
`name: ${skill.name}`,
|
|
@@ -4122,41 +4293,41 @@ function writeCursorConfig(config) {
|
|
|
4122
4293
|
"---",
|
|
4123
4294
|
""
|
|
4124
4295
|
].join("\n");
|
|
4125
|
-
|
|
4296
|
+
fs11.writeFileSync(skillPath, frontmatter + skill.content);
|
|
4126
4297
|
written.push(skillPath);
|
|
4127
4298
|
}
|
|
4128
4299
|
}
|
|
4129
4300
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
4130
4301
|
const cursorDir = ".cursor";
|
|
4131
|
-
if (!
|
|
4132
|
-
const mcpPath =
|
|
4302
|
+
if (!fs11.existsSync(cursorDir)) fs11.mkdirSync(cursorDir, { recursive: true });
|
|
4303
|
+
const mcpPath = path11.join(cursorDir, "mcp.json");
|
|
4133
4304
|
let existingServers = {};
|
|
4134
4305
|
try {
|
|
4135
|
-
if (
|
|
4136
|
-
const existing = JSON.parse(
|
|
4306
|
+
if (fs11.existsSync(mcpPath)) {
|
|
4307
|
+
const existing = JSON.parse(fs11.readFileSync(mcpPath, "utf-8"));
|
|
4137
4308
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
4138
4309
|
}
|
|
4139
4310
|
} catch {
|
|
4140
4311
|
}
|
|
4141
4312
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
4142
|
-
|
|
4313
|
+
fs11.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
4143
4314
|
written.push(mcpPath);
|
|
4144
4315
|
}
|
|
4145
4316
|
return written;
|
|
4146
4317
|
}
|
|
4147
4318
|
|
|
4148
4319
|
// src/writers/codex/index.ts
|
|
4149
|
-
import
|
|
4150
|
-
import
|
|
4320
|
+
import fs12 from "fs";
|
|
4321
|
+
import path12 from "path";
|
|
4151
4322
|
function writeCodexConfig(config) {
|
|
4152
4323
|
const written = [];
|
|
4153
|
-
|
|
4324
|
+
fs12.writeFileSync("AGENTS.md", appendLearningsBlock(appendPreCommitBlock(config.agentsMd)));
|
|
4154
4325
|
written.push("AGENTS.md");
|
|
4155
4326
|
if (config.skills?.length) {
|
|
4156
4327
|
for (const skill of config.skills) {
|
|
4157
|
-
const skillDir =
|
|
4158
|
-
if (!
|
|
4159
|
-
const skillPath =
|
|
4328
|
+
const skillDir = path12.join(".agents", "skills", skill.name);
|
|
4329
|
+
if (!fs12.existsSync(skillDir)) fs12.mkdirSync(skillDir, { recursive: true });
|
|
4330
|
+
const skillPath = path12.join(skillDir, "SKILL.md");
|
|
4160
4331
|
const frontmatter = [
|
|
4161
4332
|
"---",
|
|
4162
4333
|
`name: ${skill.name}`,
|
|
@@ -4164,7 +4335,7 @@ function writeCodexConfig(config) {
|
|
|
4164
4335
|
"---",
|
|
4165
4336
|
""
|
|
4166
4337
|
].join("\n");
|
|
4167
|
-
|
|
4338
|
+
fs12.writeFileSync(skillPath, frontmatter + skill.content);
|
|
4168
4339
|
written.push(skillPath);
|
|
4169
4340
|
}
|
|
4170
4341
|
}
|
|
@@ -4172,20 +4343,20 @@ function writeCodexConfig(config) {
|
|
|
4172
4343
|
}
|
|
4173
4344
|
|
|
4174
4345
|
// src/writers/github-copilot/index.ts
|
|
4175
|
-
import
|
|
4176
|
-
import
|
|
4346
|
+
import fs13 from "fs";
|
|
4347
|
+
import path13 from "path";
|
|
4177
4348
|
function writeGithubCopilotConfig(config) {
|
|
4178
4349
|
const written = [];
|
|
4179
4350
|
if (config.instructions) {
|
|
4180
|
-
|
|
4181
|
-
|
|
4351
|
+
fs13.mkdirSync(".github", { recursive: true });
|
|
4352
|
+
fs13.writeFileSync(path13.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(config.instructions)));
|
|
4182
4353
|
written.push(".github/copilot-instructions.md");
|
|
4183
4354
|
}
|
|
4184
4355
|
if (config.instructionFiles?.length) {
|
|
4185
|
-
const instructionsDir =
|
|
4186
|
-
|
|
4356
|
+
const instructionsDir = path13.join(".github", "instructions");
|
|
4357
|
+
fs13.mkdirSync(instructionsDir, { recursive: true });
|
|
4187
4358
|
for (const file of config.instructionFiles) {
|
|
4188
|
-
|
|
4359
|
+
fs13.writeFileSync(path13.join(instructionsDir, file.filename), file.content);
|
|
4189
4360
|
written.push(`.github/instructions/${file.filename}`);
|
|
4190
4361
|
}
|
|
4191
4362
|
}
|
|
@@ -4193,36 +4364,36 @@ function writeGithubCopilotConfig(config) {
|
|
|
4193
4364
|
}
|
|
4194
4365
|
|
|
4195
4366
|
// src/writers/backup.ts
|
|
4196
|
-
import
|
|
4197
|
-
import
|
|
4367
|
+
import fs14 from "fs";
|
|
4368
|
+
import path14 from "path";
|
|
4198
4369
|
function createBackup(files) {
|
|
4199
4370
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4200
|
-
const backupDir =
|
|
4371
|
+
const backupDir = path14.join(BACKUPS_DIR, timestamp);
|
|
4201
4372
|
for (const file of files) {
|
|
4202
|
-
if (!
|
|
4203
|
-
const dest =
|
|
4204
|
-
const destDir =
|
|
4205
|
-
if (!
|
|
4206
|
-
|
|
4373
|
+
if (!fs14.existsSync(file)) continue;
|
|
4374
|
+
const dest = path14.join(backupDir, file);
|
|
4375
|
+
const destDir = path14.dirname(dest);
|
|
4376
|
+
if (!fs14.existsSync(destDir)) {
|
|
4377
|
+
fs14.mkdirSync(destDir, { recursive: true });
|
|
4207
4378
|
}
|
|
4208
|
-
|
|
4379
|
+
fs14.copyFileSync(file, dest);
|
|
4209
4380
|
}
|
|
4210
4381
|
return backupDir;
|
|
4211
4382
|
}
|
|
4212
4383
|
function restoreBackup(backupDir, file) {
|
|
4213
|
-
const backupFile =
|
|
4214
|
-
if (!
|
|
4215
|
-
const destDir =
|
|
4216
|
-
if (!
|
|
4217
|
-
|
|
4384
|
+
const backupFile = path14.join(backupDir, file);
|
|
4385
|
+
if (!fs14.existsSync(backupFile)) return false;
|
|
4386
|
+
const destDir = path14.dirname(file);
|
|
4387
|
+
if (!fs14.existsSync(destDir)) {
|
|
4388
|
+
fs14.mkdirSync(destDir, { recursive: true });
|
|
4218
4389
|
}
|
|
4219
|
-
|
|
4390
|
+
fs14.copyFileSync(backupFile, file);
|
|
4220
4391
|
return true;
|
|
4221
4392
|
}
|
|
4222
4393
|
|
|
4223
4394
|
// src/lib/builtin-skills.ts
|
|
4224
|
-
import
|
|
4225
|
-
import
|
|
4395
|
+
import fs15 from "fs";
|
|
4396
|
+
import path15 from "path";
|
|
4226
4397
|
function buildSkillContent(skill) {
|
|
4227
4398
|
const frontmatter = `---
|
|
4228
4399
|
name: ${skill.name}
|
|
@@ -4348,19 +4519,19 @@ User: "never use any in TypeScript, use unknown instead"
|
|
|
4348
4519
|
};
|
|
4349
4520
|
var BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL];
|
|
4350
4521
|
var PLATFORM_CONFIGS = [
|
|
4351
|
-
{ platformDir: ".claude", skillsDir:
|
|
4352
|
-
{ platformDir: ".cursor", skillsDir:
|
|
4353
|
-
{ platformDir: ".agents", skillsDir:
|
|
4522
|
+
{ platformDir: ".claude", skillsDir: path15.join(".claude", "skills") },
|
|
4523
|
+
{ platformDir: ".cursor", skillsDir: path15.join(".cursor", "skills") },
|
|
4524
|
+
{ platformDir: ".agents", skillsDir: path15.join(".agents", "skills") }
|
|
4354
4525
|
];
|
|
4355
4526
|
function ensureBuiltinSkills() {
|
|
4356
4527
|
const written = [];
|
|
4357
4528
|
for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
|
|
4358
|
-
if (!
|
|
4529
|
+
if (!fs15.existsSync(platformDir)) continue;
|
|
4359
4530
|
for (const skill of BUILTIN_SKILLS) {
|
|
4360
|
-
const skillPath =
|
|
4361
|
-
if (
|
|
4362
|
-
|
|
4363
|
-
|
|
4531
|
+
const skillPath = path15.join(skillsDir, skill.name, "SKILL.md");
|
|
4532
|
+
if (fs15.existsSync(skillPath)) continue;
|
|
4533
|
+
fs15.mkdirSync(path15.dirname(skillPath), { recursive: true });
|
|
4534
|
+
fs15.writeFileSync(skillPath, buildSkillContent(skill));
|
|
4364
4535
|
written.push(skillPath);
|
|
4365
4536
|
}
|
|
4366
4537
|
}
|
|
@@ -4368,33 +4539,33 @@ function ensureBuiltinSkills() {
|
|
|
4368
4539
|
}
|
|
4369
4540
|
|
|
4370
4541
|
// src/writers/manifest.ts
|
|
4371
|
-
import
|
|
4372
|
-
import
|
|
4542
|
+
import fs16 from "fs";
|
|
4543
|
+
import crypto3 from "crypto";
|
|
4373
4544
|
function readManifest() {
|
|
4374
4545
|
try {
|
|
4375
|
-
if (!
|
|
4376
|
-
return JSON.parse(
|
|
4546
|
+
if (!fs16.existsSync(MANIFEST_FILE)) return null;
|
|
4547
|
+
return JSON.parse(fs16.readFileSync(MANIFEST_FILE, "utf-8"));
|
|
4377
4548
|
} catch {
|
|
4378
4549
|
return null;
|
|
4379
4550
|
}
|
|
4380
4551
|
}
|
|
4381
4552
|
function writeManifest(manifest) {
|
|
4382
|
-
if (!
|
|
4383
|
-
|
|
4553
|
+
if (!fs16.existsSync(CALIBER_DIR)) {
|
|
4554
|
+
fs16.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
4384
4555
|
}
|
|
4385
|
-
|
|
4556
|
+
fs16.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
|
|
4386
4557
|
}
|
|
4387
4558
|
function fileChecksum(filePath) {
|
|
4388
|
-
const content =
|
|
4389
|
-
return
|
|
4559
|
+
const content = fs16.readFileSync(filePath);
|
|
4560
|
+
return crypto3.createHash("sha256").update(content).digest("hex");
|
|
4390
4561
|
}
|
|
4391
4562
|
|
|
4392
4563
|
// src/writers/index.ts
|
|
4393
4564
|
function writeSetup(setup) {
|
|
4394
4565
|
const filesToWrite = getFilesToWrite(setup);
|
|
4395
|
-
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) =>
|
|
4566
|
+
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs17.existsSync(f));
|
|
4396
4567
|
const existingFiles = [
|
|
4397
|
-
...filesToWrite.filter((f) =>
|
|
4568
|
+
...filesToWrite.filter((f) => fs17.existsSync(f)),
|
|
4398
4569
|
...filesToDelete
|
|
4399
4570
|
];
|
|
4400
4571
|
const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
|
|
@@ -4413,7 +4584,7 @@ function writeSetup(setup) {
|
|
|
4413
4584
|
}
|
|
4414
4585
|
const deleted = [];
|
|
4415
4586
|
for (const filePath of filesToDelete) {
|
|
4416
|
-
|
|
4587
|
+
fs17.unlinkSync(filePath);
|
|
4417
4588
|
deleted.push(filePath);
|
|
4418
4589
|
}
|
|
4419
4590
|
written.push(...ensureBuiltinSkills());
|
|
@@ -4444,8 +4615,8 @@ function undoSetup() {
|
|
|
4444
4615
|
const removed = [];
|
|
4445
4616
|
for (const entry of manifest.entries) {
|
|
4446
4617
|
if (entry.action === "created") {
|
|
4447
|
-
if (
|
|
4448
|
-
|
|
4618
|
+
if (fs17.existsSync(entry.path)) {
|
|
4619
|
+
fs17.unlinkSync(entry.path);
|
|
4449
4620
|
removed.push(entry.path);
|
|
4450
4621
|
}
|
|
4451
4622
|
} else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
|
|
@@ -4454,8 +4625,8 @@ function undoSetup() {
|
|
|
4454
4625
|
}
|
|
4455
4626
|
}
|
|
4456
4627
|
}
|
|
4457
|
-
if (
|
|
4458
|
-
|
|
4628
|
+
if (fs17.existsSync(MANIFEST_FILE)) {
|
|
4629
|
+
fs17.unlinkSync(MANIFEST_FILE);
|
|
4459
4630
|
}
|
|
4460
4631
|
return { restored, removed };
|
|
4461
4632
|
}
|
|
@@ -4496,22 +4667,22 @@ function getFilesToWrite(setup) {
|
|
|
4496
4667
|
}
|
|
4497
4668
|
function ensureGitignore() {
|
|
4498
4669
|
const gitignorePath = ".gitignore";
|
|
4499
|
-
if (
|
|
4500
|
-
const content =
|
|
4670
|
+
if (fs17.existsSync(gitignorePath)) {
|
|
4671
|
+
const content = fs17.readFileSync(gitignorePath, "utf-8");
|
|
4501
4672
|
if (!content.includes(".caliber/")) {
|
|
4502
|
-
|
|
4673
|
+
fs17.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
|
|
4503
4674
|
}
|
|
4504
4675
|
} else {
|
|
4505
|
-
|
|
4676
|
+
fs17.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
|
|
4506
4677
|
}
|
|
4507
4678
|
}
|
|
4508
4679
|
|
|
4509
4680
|
// src/writers/staging.ts
|
|
4510
|
-
import
|
|
4511
|
-
import
|
|
4512
|
-
var STAGED_DIR =
|
|
4513
|
-
var PROPOSED_DIR =
|
|
4514
|
-
var CURRENT_DIR =
|
|
4681
|
+
import fs18 from "fs";
|
|
4682
|
+
import path16 from "path";
|
|
4683
|
+
var STAGED_DIR = path16.join(CALIBER_DIR, "staged");
|
|
4684
|
+
var PROPOSED_DIR = path16.join(STAGED_DIR, "proposed");
|
|
4685
|
+
var CURRENT_DIR = path16.join(STAGED_DIR, "current");
|
|
4515
4686
|
function normalizeContent(content) {
|
|
4516
4687
|
return content.split("\n").map((line) => line.trimEnd()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
4517
4688
|
}
|
|
@@ -4521,20 +4692,20 @@ function stageFiles(files, projectDir) {
|
|
|
4521
4692
|
let modifiedFiles = 0;
|
|
4522
4693
|
const stagedFiles = [];
|
|
4523
4694
|
for (const file of files) {
|
|
4524
|
-
const originalPath =
|
|
4525
|
-
if (
|
|
4526
|
-
const existing =
|
|
4695
|
+
const originalPath = path16.join(projectDir, file.path);
|
|
4696
|
+
if (fs18.existsSync(originalPath)) {
|
|
4697
|
+
const existing = fs18.readFileSync(originalPath, "utf-8");
|
|
4527
4698
|
if (normalizeContent(existing) === normalizeContent(file.content)) {
|
|
4528
4699
|
continue;
|
|
4529
4700
|
}
|
|
4530
4701
|
}
|
|
4531
|
-
const proposedPath =
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
if (
|
|
4535
|
-
const currentPath =
|
|
4536
|
-
|
|
4537
|
-
|
|
4702
|
+
const proposedPath = path16.join(PROPOSED_DIR, file.path);
|
|
4703
|
+
fs18.mkdirSync(path16.dirname(proposedPath), { recursive: true });
|
|
4704
|
+
fs18.writeFileSync(proposedPath, file.content);
|
|
4705
|
+
if (fs18.existsSync(originalPath)) {
|
|
4706
|
+
const currentPath = path16.join(CURRENT_DIR, file.path);
|
|
4707
|
+
fs18.mkdirSync(path16.dirname(currentPath), { recursive: true });
|
|
4708
|
+
fs18.copyFileSync(originalPath, currentPath);
|
|
4538
4709
|
modifiedFiles++;
|
|
4539
4710
|
stagedFiles.push({ relativePath: file.path, proposedPath, currentPath, originalPath, isNew: false });
|
|
4540
4711
|
} else {
|
|
@@ -4545,13 +4716,13 @@ function stageFiles(files, projectDir) {
|
|
|
4545
4716
|
return { newFiles, modifiedFiles, stagedFiles };
|
|
4546
4717
|
}
|
|
4547
4718
|
function cleanupStaging() {
|
|
4548
|
-
if (
|
|
4549
|
-
|
|
4719
|
+
if (fs18.existsSync(STAGED_DIR)) {
|
|
4720
|
+
fs18.rmSync(STAGED_DIR, { recursive: true, force: true });
|
|
4550
4721
|
}
|
|
4551
4722
|
}
|
|
4552
4723
|
|
|
4553
4724
|
// src/commands/setup-files.ts
|
|
4554
|
-
import
|
|
4725
|
+
import fs19 from "fs";
|
|
4555
4726
|
function collectSetupFiles(setup, targetAgent) {
|
|
4556
4727
|
const files = [];
|
|
4557
4728
|
const claude = setup.claude;
|
|
@@ -4610,7 +4781,7 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
4610
4781
|
}
|
|
4611
4782
|
}
|
|
4612
4783
|
const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
|
|
4613
|
-
if (codexTargeted && !
|
|
4784
|
+
if (codexTargeted && !fs19.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
|
|
4614
4785
|
const agentRefs = [];
|
|
4615
4786
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
4616
4787
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
@@ -4628,9 +4799,9 @@ ${agentRefs.join(" ")}
|
|
|
4628
4799
|
|
|
4629
4800
|
// src/lib/learning-hooks.ts
|
|
4630
4801
|
init_resolve_caliber();
|
|
4631
|
-
import
|
|
4632
|
-
import
|
|
4633
|
-
var SETTINGS_PATH =
|
|
4802
|
+
import fs21 from "fs";
|
|
4803
|
+
import path17 from "path";
|
|
4804
|
+
var SETTINGS_PATH = path17.join(".claude", "settings.json");
|
|
4634
4805
|
var HOOK_TAILS = [
|
|
4635
4806
|
{ event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
|
|
4636
4807
|
{ event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
|
|
@@ -4647,17 +4818,17 @@ function getHookConfigs() {
|
|
|
4647
4818
|
}));
|
|
4648
4819
|
}
|
|
4649
4820
|
function readSettings() {
|
|
4650
|
-
if (!
|
|
4821
|
+
if (!fs21.existsSync(SETTINGS_PATH)) return {};
|
|
4651
4822
|
try {
|
|
4652
|
-
return JSON.parse(
|
|
4823
|
+
return JSON.parse(fs21.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
4653
4824
|
} catch {
|
|
4654
4825
|
return {};
|
|
4655
4826
|
}
|
|
4656
4827
|
}
|
|
4657
4828
|
function writeSettings(settings) {
|
|
4658
|
-
const dir =
|
|
4659
|
-
if (!
|
|
4660
|
-
|
|
4829
|
+
const dir = path17.dirname(SETTINGS_PATH);
|
|
4830
|
+
if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
|
|
4831
|
+
fs21.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
4661
4832
|
}
|
|
4662
4833
|
function hasLearningHook(matchers, tail) {
|
|
4663
4834
|
return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
|
|
@@ -4691,7 +4862,7 @@ function installLearningHooks() {
|
|
|
4691
4862
|
writeSettings(settings);
|
|
4692
4863
|
return { installed: true, alreadyInstalled: false };
|
|
4693
4864
|
}
|
|
4694
|
-
var CURSOR_HOOKS_PATH =
|
|
4865
|
+
var CURSOR_HOOKS_PATH = path17.join(".cursor", "hooks.json");
|
|
4695
4866
|
var CURSOR_HOOK_EVENTS = [
|
|
4696
4867
|
{ event: "postToolUse", tail: "learn observe" },
|
|
4697
4868
|
{ event: "postToolUseFailure", tail: "learn observe --failure" },
|
|
@@ -4699,17 +4870,17 @@ var CURSOR_HOOK_EVENTS = [
|
|
|
4699
4870
|
{ event: "sessionEnd", tail: "learn finalize --auto" }
|
|
4700
4871
|
];
|
|
4701
4872
|
function readCursorHooks() {
|
|
4702
|
-
if (!
|
|
4873
|
+
if (!fs21.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
|
|
4703
4874
|
try {
|
|
4704
|
-
return JSON.parse(
|
|
4875
|
+
return JSON.parse(fs21.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
|
|
4705
4876
|
} catch {
|
|
4706
4877
|
return { version: 1, hooks: {} };
|
|
4707
4878
|
}
|
|
4708
4879
|
}
|
|
4709
4880
|
function writeCursorHooks(config) {
|
|
4710
|
-
const dir =
|
|
4711
|
-
if (!
|
|
4712
|
-
|
|
4881
|
+
const dir = path17.dirname(CURSOR_HOOKS_PATH);
|
|
4882
|
+
if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
|
|
4883
|
+
fs21.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
|
|
4713
4884
|
}
|
|
4714
4885
|
function hasCursorHook(entries, tail) {
|
|
4715
4886
|
return entries.some((e) => isCaliberCommand(e.command, tail));
|
|
@@ -4778,10 +4949,10 @@ function removeLearningHooks() {
|
|
|
4778
4949
|
}
|
|
4779
4950
|
|
|
4780
4951
|
// src/lib/state.ts
|
|
4781
|
-
import
|
|
4782
|
-
import
|
|
4952
|
+
import fs22 from "fs";
|
|
4953
|
+
import path18 from "path";
|
|
4783
4954
|
import { execSync as execSync7 } from "child_process";
|
|
4784
|
-
var STATE_FILE =
|
|
4955
|
+
var STATE_FILE = path18.join(CALIBER_DIR, ".caliber-state.json");
|
|
4785
4956
|
function normalizeTargetAgent(value) {
|
|
4786
4957
|
if (Array.isArray(value)) return value;
|
|
4787
4958
|
if (typeof value === "string") {
|
|
@@ -4792,8 +4963,8 @@ function normalizeTargetAgent(value) {
|
|
|
4792
4963
|
}
|
|
4793
4964
|
function readState() {
|
|
4794
4965
|
try {
|
|
4795
|
-
if (!
|
|
4796
|
-
const raw = JSON.parse(
|
|
4966
|
+
if (!fs22.existsSync(STATE_FILE)) return null;
|
|
4967
|
+
const raw = JSON.parse(fs22.readFileSync(STATE_FILE, "utf-8"));
|
|
4797
4968
|
if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
|
|
4798
4969
|
return raw;
|
|
4799
4970
|
} catch {
|
|
@@ -4801,10 +4972,10 @@ function readState() {
|
|
|
4801
4972
|
}
|
|
4802
4973
|
}
|
|
4803
4974
|
function writeState(state) {
|
|
4804
|
-
if (!
|
|
4805
|
-
|
|
4975
|
+
if (!fs22.existsSync(CALIBER_DIR)) {
|
|
4976
|
+
fs22.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
4806
4977
|
}
|
|
4807
|
-
|
|
4978
|
+
fs22.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
4808
4979
|
}
|
|
4809
4980
|
function getCurrentHeadSha() {
|
|
4810
4981
|
try {
|
|
@@ -5862,22 +6033,22 @@ function checkSources(dir) {
|
|
|
5862
6033
|
}
|
|
5863
6034
|
|
|
5864
6035
|
// src/scoring/dismissed.ts
|
|
5865
|
-
import
|
|
5866
|
-
import
|
|
5867
|
-
var DISMISSED_FILE =
|
|
6036
|
+
import fs23 from "fs";
|
|
6037
|
+
import path19 from "path";
|
|
6038
|
+
var DISMISSED_FILE = path19.join(CALIBER_DIR, "dismissed-checks.json");
|
|
5868
6039
|
function readDismissedChecks() {
|
|
5869
6040
|
try {
|
|
5870
|
-
if (!
|
|
5871
|
-
return JSON.parse(
|
|
6041
|
+
if (!fs23.existsSync(DISMISSED_FILE)) return [];
|
|
6042
|
+
return JSON.parse(fs23.readFileSync(DISMISSED_FILE, "utf-8"));
|
|
5872
6043
|
} catch {
|
|
5873
6044
|
return [];
|
|
5874
6045
|
}
|
|
5875
6046
|
}
|
|
5876
6047
|
function writeDismissedChecks(checks) {
|
|
5877
|
-
if (!
|
|
5878
|
-
|
|
6048
|
+
if (!fs23.existsSync(CALIBER_DIR)) {
|
|
6049
|
+
fs23.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
5879
6050
|
}
|
|
5880
|
-
|
|
6051
|
+
fs23.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
|
|
5881
6052
|
}
|
|
5882
6053
|
function getDismissedIds() {
|
|
5883
6054
|
return new Set(readDismissedChecks().map((c) => c.id));
|
|
@@ -6123,160 +6294,6 @@ import ora from "ora";
|
|
|
6123
6294
|
import select3 from "@inquirer/select";
|
|
6124
6295
|
import { mkdirSync, readFileSync as readFileSync4, readdirSync as readdirSync4, existsSync as existsSync8, writeFileSync } from "fs";
|
|
6125
6296
|
import { join as join10, dirname as dirname2 } from "path";
|
|
6126
|
-
|
|
6127
|
-
// src/scanner/index.ts
|
|
6128
|
-
import fs23 from "fs";
|
|
6129
|
-
import path19 from "path";
|
|
6130
|
-
import crypto3 from "crypto";
|
|
6131
|
-
function scanLocalState(dir) {
|
|
6132
|
-
const items = [];
|
|
6133
|
-
const claudeMdPath = path19.join(dir, "CLAUDE.md");
|
|
6134
|
-
if (fs23.existsSync(claudeMdPath)) {
|
|
6135
|
-
items.push({
|
|
6136
|
-
type: "rule",
|
|
6137
|
-
platform: "claude",
|
|
6138
|
-
name: "CLAUDE.md",
|
|
6139
|
-
contentHash: hashFile(claudeMdPath),
|
|
6140
|
-
path: claudeMdPath
|
|
6141
|
-
});
|
|
6142
|
-
}
|
|
6143
|
-
const skillsDir = path19.join(dir, ".claude", "skills");
|
|
6144
|
-
if (fs23.existsSync(skillsDir)) {
|
|
6145
|
-
for (const file of fs23.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
6146
|
-
const filePath = path19.join(skillsDir, file);
|
|
6147
|
-
items.push({
|
|
6148
|
-
type: "skill",
|
|
6149
|
-
platform: "claude",
|
|
6150
|
-
name: file,
|
|
6151
|
-
contentHash: hashFile(filePath),
|
|
6152
|
-
path: filePath
|
|
6153
|
-
});
|
|
6154
|
-
}
|
|
6155
|
-
}
|
|
6156
|
-
const mcpJsonPath = path19.join(dir, ".mcp.json");
|
|
6157
|
-
if (fs23.existsSync(mcpJsonPath)) {
|
|
6158
|
-
try {
|
|
6159
|
-
const mcpJson = JSON.parse(fs23.readFileSync(mcpJsonPath, "utf-8"));
|
|
6160
|
-
if (mcpJson.mcpServers) {
|
|
6161
|
-
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
6162
|
-
items.push({
|
|
6163
|
-
type: "mcp",
|
|
6164
|
-
platform: "claude",
|
|
6165
|
-
name,
|
|
6166
|
-
contentHash: hashJson(mcpJson.mcpServers[name]),
|
|
6167
|
-
path: mcpJsonPath
|
|
6168
|
-
});
|
|
6169
|
-
}
|
|
6170
|
-
}
|
|
6171
|
-
} catch (error) {
|
|
6172
|
-
warnScanSkip(".mcp.json", error);
|
|
6173
|
-
}
|
|
6174
|
-
}
|
|
6175
|
-
const agentsMdPath = path19.join(dir, "AGENTS.md");
|
|
6176
|
-
if (fs23.existsSync(agentsMdPath)) {
|
|
6177
|
-
items.push({
|
|
6178
|
-
type: "rule",
|
|
6179
|
-
platform: "codex",
|
|
6180
|
-
name: "AGENTS.md",
|
|
6181
|
-
contentHash: hashFile(agentsMdPath),
|
|
6182
|
-
path: agentsMdPath
|
|
6183
|
-
});
|
|
6184
|
-
}
|
|
6185
|
-
const codexSkillsDir = path19.join(dir, ".agents", "skills");
|
|
6186
|
-
if (fs23.existsSync(codexSkillsDir)) {
|
|
6187
|
-
try {
|
|
6188
|
-
for (const name of fs23.readdirSync(codexSkillsDir)) {
|
|
6189
|
-
const skillFile = path19.join(codexSkillsDir, name, "SKILL.md");
|
|
6190
|
-
if (fs23.existsSync(skillFile)) {
|
|
6191
|
-
items.push({
|
|
6192
|
-
type: "skill",
|
|
6193
|
-
platform: "codex",
|
|
6194
|
-
name: `${name}/SKILL.md`,
|
|
6195
|
-
contentHash: hashFile(skillFile),
|
|
6196
|
-
path: skillFile
|
|
6197
|
-
});
|
|
6198
|
-
}
|
|
6199
|
-
}
|
|
6200
|
-
} catch (error) {
|
|
6201
|
-
warnScanSkip(".agents/skills", error);
|
|
6202
|
-
}
|
|
6203
|
-
}
|
|
6204
|
-
const cursorrulesPath = path19.join(dir, ".cursorrules");
|
|
6205
|
-
if (fs23.existsSync(cursorrulesPath)) {
|
|
6206
|
-
items.push({
|
|
6207
|
-
type: "rule",
|
|
6208
|
-
platform: "cursor",
|
|
6209
|
-
name: ".cursorrules",
|
|
6210
|
-
contentHash: hashFile(cursorrulesPath),
|
|
6211
|
-
path: cursorrulesPath
|
|
6212
|
-
});
|
|
6213
|
-
}
|
|
6214
|
-
const cursorRulesDir = path19.join(dir, ".cursor", "rules");
|
|
6215
|
-
if (fs23.existsSync(cursorRulesDir)) {
|
|
6216
|
-
for (const file of fs23.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
6217
|
-
const filePath = path19.join(cursorRulesDir, file);
|
|
6218
|
-
items.push({
|
|
6219
|
-
type: "rule",
|
|
6220
|
-
platform: "cursor",
|
|
6221
|
-
name: file,
|
|
6222
|
-
contentHash: hashFile(filePath),
|
|
6223
|
-
path: filePath
|
|
6224
|
-
});
|
|
6225
|
-
}
|
|
6226
|
-
}
|
|
6227
|
-
const cursorSkillsDir = path19.join(dir, ".cursor", "skills");
|
|
6228
|
-
if (fs23.existsSync(cursorSkillsDir)) {
|
|
6229
|
-
try {
|
|
6230
|
-
for (const name of fs23.readdirSync(cursorSkillsDir)) {
|
|
6231
|
-
const skillFile = path19.join(cursorSkillsDir, name, "SKILL.md");
|
|
6232
|
-
if (fs23.existsSync(skillFile)) {
|
|
6233
|
-
items.push({
|
|
6234
|
-
type: "skill",
|
|
6235
|
-
platform: "cursor",
|
|
6236
|
-
name: `${name}/SKILL.md`,
|
|
6237
|
-
contentHash: hashFile(skillFile),
|
|
6238
|
-
path: skillFile
|
|
6239
|
-
});
|
|
6240
|
-
}
|
|
6241
|
-
}
|
|
6242
|
-
} catch (error) {
|
|
6243
|
-
warnScanSkip(".cursor/skills", error);
|
|
6244
|
-
}
|
|
6245
|
-
}
|
|
6246
|
-
const cursorMcpPath = path19.join(dir, ".cursor", "mcp.json");
|
|
6247
|
-
if (fs23.existsSync(cursorMcpPath)) {
|
|
6248
|
-
try {
|
|
6249
|
-
const mcpJson = JSON.parse(fs23.readFileSync(cursorMcpPath, "utf-8"));
|
|
6250
|
-
if (mcpJson.mcpServers) {
|
|
6251
|
-
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
6252
|
-
items.push({
|
|
6253
|
-
type: "mcp",
|
|
6254
|
-
platform: "cursor",
|
|
6255
|
-
name,
|
|
6256
|
-
contentHash: hashJson(mcpJson.mcpServers[name]),
|
|
6257
|
-
path: cursorMcpPath
|
|
6258
|
-
});
|
|
6259
|
-
}
|
|
6260
|
-
}
|
|
6261
|
-
} catch (error) {
|
|
6262
|
-
warnScanSkip(".cursor/mcp.json", error);
|
|
6263
|
-
}
|
|
6264
|
-
}
|
|
6265
|
-
return items;
|
|
6266
|
-
}
|
|
6267
|
-
function hashFile(filePath) {
|
|
6268
|
-
const text = fs23.readFileSync(filePath, "utf-8");
|
|
6269
|
-
return crypto3.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
6270
|
-
}
|
|
6271
|
-
function hashJson(obj) {
|
|
6272
|
-
return crypto3.createHash("sha256").update(JSON.stringify(obj)).digest("hex");
|
|
6273
|
-
}
|
|
6274
|
-
function warnScanSkip(target, error) {
|
|
6275
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
6276
|
-
console.warn(`Warning: ${target} scan skipped (${message})`);
|
|
6277
|
-
}
|
|
6278
|
-
|
|
6279
|
-
// src/commands/recommend.ts
|
|
6280
6297
|
init_config();
|
|
6281
6298
|
|
|
6282
6299
|
// src/telemetry/index.ts
|
|
@@ -6286,10 +6303,10 @@ import chalk5 from "chalk";
|
|
|
6286
6303
|
// src/telemetry/config.ts
|
|
6287
6304
|
import fs24 from "fs";
|
|
6288
6305
|
import path20 from "path";
|
|
6289
|
-
import
|
|
6306
|
+
import os5 from "os";
|
|
6290
6307
|
import crypto4 from "crypto";
|
|
6291
6308
|
import { execSync as execSync11 } from "child_process";
|
|
6292
|
-
var CONFIG_DIR2 = path20.join(
|
|
6309
|
+
var CONFIG_DIR2 = path20.join(os5.homedir(), ".caliber");
|
|
6293
6310
|
var CONFIG_FILE2 = path20.join(CONFIG_DIR2, "config.json");
|
|
6294
6311
|
var runtimeDisabled = false;
|
|
6295
6312
|
function readConfig() {
|
|
@@ -8569,6 +8586,11 @@ async function initCommand(options) {
|
|
|
8569
8586
|
} else {
|
|
8570
8587
|
console.log(brand.bold("\n CALIBER") + chalk14.dim(" \u2014 regenerating config\n"));
|
|
8571
8588
|
}
|
|
8589
|
+
const platforms = detectPlatforms();
|
|
8590
|
+
if (!platforms.claude && !platforms.cursor && !platforms.codex) {
|
|
8591
|
+
console.log(chalk14.yellow(" \u26A0 No supported AI platforms detected (Claude, Cursor, Codex)."));
|
|
8592
|
+
console.log(chalk14.yellow(" Caliber will still generate config files, but they won't be auto-installed.\n"));
|
|
8593
|
+
}
|
|
8572
8594
|
const report = options.debugReport ? new DebugReport() : null;
|
|
8573
8595
|
console.log(title.bold(" Step 1/4 \u2014 Setup\n"));
|
|
8574
8596
|
let config = loadConfig();
|
|
@@ -9304,7 +9326,7 @@ async function regenerateCommand(options) {
|
|
|
9304
9326
|
|
|
9305
9327
|
// src/commands/score.ts
|
|
9306
9328
|
import fs33 from "fs";
|
|
9307
|
-
import
|
|
9329
|
+
import os7 from "os";
|
|
9308
9330
|
import path25 from "path";
|
|
9309
9331
|
import { execFileSync } from "child_process";
|
|
9310
9332
|
import chalk18 from "chalk";
|
|
@@ -9312,7 +9334,7 @@ var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS
|
|
|
9312
9334
|
var CONFIG_DIRS = [".claude", ".cursor"];
|
|
9313
9335
|
function scoreBaseRef(ref, target) {
|
|
9314
9336
|
if (!/^[\w.\-\/~^@{}]+$/.test(ref)) return null;
|
|
9315
|
-
const tmpDir = fs33.mkdtempSync(path25.join(
|
|
9337
|
+
const tmpDir = fs33.mkdtempSync(path25.join(os7.tmpdir(), "caliber-compare-"));
|
|
9316
9338
|
try {
|
|
9317
9339
|
for (const file of CONFIG_FILES) {
|
|
9318
9340
|
try {
|
package/package.json
CHANGED