@rely-ai/caliber 1.41.4 → 1.42.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 +673 -467
- package/package.json +2 -1
package/dist/bin.js
CHANGED
|
@@ -169,19 +169,44 @@ import fs6 from "fs";
|
|
|
169
169
|
import { execSync as execSync4 } from "child_process";
|
|
170
170
|
function resolveCaliber() {
|
|
171
171
|
if (_resolved) return _resolved;
|
|
172
|
+
const whichCmd = process.platform === "win32" ? "where caliber" : "which caliber";
|
|
173
|
+
const whichNpxCmd = process.platform === "win32" ? "where npx" : "which npx";
|
|
172
174
|
const isNpx = process.argv[1]?.includes("_npx") || process.env.npm_execpath?.includes("npx");
|
|
173
175
|
if (isNpx) {
|
|
176
|
+
try {
|
|
177
|
+
const out = execSync4(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
178
|
+
const caliberPath = out.split("\n")[0].trim();
|
|
179
|
+
if (caliberPath) {
|
|
180
|
+
_resolved = caliberPath;
|
|
181
|
+
return _resolved;
|
|
182
|
+
}
|
|
183
|
+
} catch {
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
const out = execSync4(whichNpxCmd, {
|
|
187
|
+
encoding: "utf-8",
|
|
188
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
189
|
+
}).trim();
|
|
190
|
+
const npxPath = out.split("\n")[0].trim();
|
|
191
|
+
if (npxPath) {
|
|
192
|
+
_resolved = `${npxPath} --yes @rely-ai/caliber`;
|
|
193
|
+
return _resolved;
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
}
|
|
174
197
|
_resolved = "npx --yes @rely-ai/caliber";
|
|
175
198
|
return _resolved;
|
|
176
199
|
}
|
|
177
200
|
try {
|
|
178
|
-
const
|
|
179
|
-
execSync4(whichCmd, {
|
|
201
|
+
const out = execSync4(whichCmd, {
|
|
180
202
|
encoding: "utf-8",
|
|
181
203
|
stdio: ["pipe", "pipe", "pipe"]
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
|
|
204
|
+
}).trim();
|
|
205
|
+
const caliberPath = out.split("\n")[0].trim();
|
|
206
|
+
if (caliberPath) {
|
|
207
|
+
_resolved = caliberPath;
|
|
208
|
+
return _resolved;
|
|
209
|
+
}
|
|
185
210
|
} catch {
|
|
186
211
|
}
|
|
187
212
|
const binPath = process.argv[1];
|
|
@@ -193,7 +218,8 @@ function resolveCaliber() {
|
|
|
193
218
|
return _resolved;
|
|
194
219
|
}
|
|
195
220
|
function isNpxResolution() {
|
|
196
|
-
|
|
221
|
+
const r = resolveCaliber();
|
|
222
|
+
return r === "npx --yes @rely-ai/caliber" || r.endsWith("/npx --yes @rely-ai/caliber");
|
|
197
223
|
}
|
|
198
224
|
function resetResolvedCaliber() {
|
|
199
225
|
_resolved = null;
|
|
@@ -203,6 +229,8 @@ function isCaliberCommand(command, subcommandTail) {
|
|
|
203
229
|
if (command.endsWith(`/caliber ${subcommandTail}`)) return true;
|
|
204
230
|
if (command === `npx --yes @rely-ai/caliber ${subcommandTail}`) return true;
|
|
205
231
|
if (command === `npx @rely-ai/caliber ${subcommandTail}`) return true;
|
|
232
|
+
if (command.endsWith(`/npx --yes @rely-ai/caliber ${subcommandTail}`)) return true;
|
|
233
|
+
if (command.endsWith(`/npx @rely-ai/caliber ${subcommandTail}`)) return true;
|
|
206
234
|
return false;
|
|
207
235
|
}
|
|
208
236
|
var _resolved;
|
|
@@ -445,7 +473,7 @@ __export(builtin_skills_exports, {
|
|
|
445
473
|
buildSkillContent: () => buildSkillContent,
|
|
446
474
|
ensureBuiltinSkills: () => ensureBuiltinSkills
|
|
447
475
|
});
|
|
448
|
-
import
|
|
476
|
+
import fs17 from "fs";
|
|
449
477
|
import path16 from "path";
|
|
450
478
|
function buildSkillContent(skill) {
|
|
451
479
|
const frontmatter = `---
|
|
@@ -779,11 +807,11 @@ From now on, every commit keeps all your agent configs in sync automatically.
|
|
|
779
807
|
function ensureBuiltinSkills() {
|
|
780
808
|
const written = [];
|
|
781
809
|
for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
|
|
782
|
-
if (!
|
|
810
|
+
if (!fs17.existsSync(platformDir)) continue;
|
|
783
811
|
for (const skill of BUILTIN_SKILLS) {
|
|
784
812
|
const skillPath = path16.join(skillsDir, skill.name, "SKILL.md");
|
|
785
|
-
|
|
786
|
-
|
|
813
|
+
fs17.mkdirSync(path16.dirname(skillPath), { recursive: true });
|
|
814
|
+
fs17.writeFileSync(skillPath, buildSkillContent(skill));
|
|
787
815
|
written.push(skillPath);
|
|
788
816
|
}
|
|
789
817
|
}
|
|
@@ -828,13 +856,13 @@ var init_builtin_skills = __esm({
|
|
|
828
856
|
|
|
829
857
|
// src/utils/editor.ts
|
|
830
858
|
import { execSync as execSync14, spawn as spawn3 } from "child_process";
|
|
831
|
-
import
|
|
859
|
+
import fs29 from "fs";
|
|
832
860
|
import path25 from "path";
|
|
833
861
|
import os6 from "os";
|
|
834
862
|
function getEmptyFilePath(proposedPath) {
|
|
835
|
-
|
|
863
|
+
fs29.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
|
|
836
864
|
const tempPath = path25.join(DIFF_TEMP_DIR, path25.basename(proposedPath));
|
|
837
|
-
|
|
865
|
+
fs29.writeFileSync(tempPath, "");
|
|
838
866
|
return tempPath;
|
|
839
867
|
}
|
|
840
868
|
function commandExists(cmd) {
|
|
@@ -886,10 +914,11 @@ __export(review_exports, {
|
|
|
886
914
|
promptWantsReview: () => promptWantsReview
|
|
887
915
|
});
|
|
888
916
|
import chalk10 from "chalk";
|
|
889
|
-
import
|
|
917
|
+
import fs30 from "fs";
|
|
890
918
|
import select4 from "@inquirer/select";
|
|
891
919
|
import { createTwoFilesPatch } from "diff";
|
|
892
920
|
async function promptWantsReview() {
|
|
921
|
+
if (!process.stdin.isTTY) return false;
|
|
893
922
|
return select4({
|
|
894
923
|
message: "Would you like to review the diffs before deciding?",
|
|
895
924
|
choices: [
|
|
@@ -901,6 +930,7 @@ async function promptWantsReview() {
|
|
|
901
930
|
async function promptReviewMethod() {
|
|
902
931
|
const available = detectAvailableEditors();
|
|
903
932
|
if (available.length === 1) return "terminal";
|
|
933
|
+
if (!process.stdin.isTTY) return "terminal";
|
|
904
934
|
const choices = available.map((method) => {
|
|
905
935
|
switch (method) {
|
|
906
936
|
case "cursor":
|
|
@@ -915,16 +945,19 @@ async function promptReviewMethod() {
|
|
|
915
945
|
}
|
|
916
946
|
async function openReview(method, stagedFiles) {
|
|
917
947
|
if (method === "cursor" || method === "vscode") {
|
|
918
|
-
openDiffsInEditor(
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
948
|
+
openDiffsInEditor(
|
|
949
|
+
method,
|
|
950
|
+
stagedFiles.map((f) => ({
|
|
951
|
+
originalPath: f.originalPath,
|
|
952
|
+
proposedPath: f.proposedPath
|
|
953
|
+
}))
|
|
954
|
+
);
|
|
922
955
|
console.log(chalk10.dim(" Diffs opened in your editor.\n"));
|
|
923
956
|
return;
|
|
924
957
|
}
|
|
925
958
|
const fileInfos = stagedFiles.map((file) => {
|
|
926
|
-
const proposed =
|
|
927
|
-
const current = file.currentPath ?
|
|
959
|
+
const proposed = fs30.readFileSync(file.proposedPath, "utf-8");
|
|
960
|
+
const current = file.currentPath ? fs30.readFileSync(file.currentPath, "utf-8") : "";
|
|
928
961
|
const patch = createTwoFilesPatch(
|
|
929
962
|
file.isNew ? "/dev/null" : file.relativePath,
|
|
930
963
|
file.relativePath,
|
|
@@ -1099,7 +1132,7 @@ __export(lock_exports, {
|
|
|
1099
1132
|
isCaliberRunning: () => isCaliberRunning,
|
|
1100
1133
|
releaseLock: () => releaseLock
|
|
1101
1134
|
});
|
|
1102
|
-
import
|
|
1135
|
+
import fs41 from "fs";
|
|
1103
1136
|
import path33 from "path";
|
|
1104
1137
|
import os8 from "os";
|
|
1105
1138
|
import crypto5 from "crypto";
|
|
@@ -1115,8 +1148,8 @@ function getLockFile() {
|
|
|
1115
1148
|
function isCaliberRunning() {
|
|
1116
1149
|
try {
|
|
1117
1150
|
const lockFile = buildLockPath();
|
|
1118
|
-
if (!
|
|
1119
|
-
const raw =
|
|
1151
|
+
if (!fs41.existsSync(lockFile)) return false;
|
|
1152
|
+
const raw = fs41.readFileSync(lockFile, "utf-8").trim();
|
|
1120
1153
|
const { pid, ts } = JSON.parse(raw);
|
|
1121
1154
|
if (pid === process.pid) return false;
|
|
1122
1155
|
if (Date.now() - ts > STALE_MS) return false;
|
|
@@ -1132,14 +1165,14 @@ function isCaliberRunning() {
|
|
|
1132
1165
|
}
|
|
1133
1166
|
function acquireLock() {
|
|
1134
1167
|
try {
|
|
1135
|
-
|
|
1168
|
+
fs41.writeFileSync(getLockFile(), JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
1136
1169
|
} catch {
|
|
1137
1170
|
}
|
|
1138
1171
|
}
|
|
1139
1172
|
function releaseLock() {
|
|
1140
1173
|
try {
|
|
1141
1174
|
const lockFile = getLockFile();
|
|
1142
|
-
if (
|
|
1175
|
+
if (fs41.existsSync(lockFile)) fs41.unlinkSync(lockFile);
|
|
1143
1176
|
} catch {
|
|
1144
1177
|
}
|
|
1145
1178
|
}
|
|
@@ -1154,17 +1187,17 @@ var init_lock = __esm({
|
|
|
1154
1187
|
|
|
1155
1188
|
// src/cli.ts
|
|
1156
1189
|
import { Command } from "commander";
|
|
1157
|
-
import
|
|
1190
|
+
import fs52 from "fs";
|
|
1158
1191
|
import path42 from "path";
|
|
1159
1192
|
import { fileURLToPath } from "url";
|
|
1160
1193
|
|
|
1161
1194
|
// src/commands/init.ts
|
|
1162
1195
|
import path28 from "path";
|
|
1163
1196
|
import chalk14 from "chalk";
|
|
1164
|
-
import
|
|
1197
|
+
import fs35 from "fs";
|
|
1165
1198
|
|
|
1166
1199
|
// src/fingerprint/index.ts
|
|
1167
|
-
import
|
|
1200
|
+
import fs9 from "fs";
|
|
1168
1201
|
import path8 from "path";
|
|
1169
1202
|
|
|
1170
1203
|
// src/fingerprint/git.ts
|
|
@@ -2358,7 +2391,7 @@ var OpenAICompatProvider = class {
|
|
|
2358
2391
|
};
|
|
2359
2392
|
|
|
2360
2393
|
// src/llm/cursor-acp.ts
|
|
2361
|
-
import { spawn, execSync as execSync5 } from "child_process";
|
|
2394
|
+
import { spawn, execSync as execSync5, execFileSync } from "child_process";
|
|
2362
2395
|
import os3 from "os";
|
|
2363
2396
|
|
|
2364
2397
|
// src/llm/seat-based-errors.ts
|
|
@@ -2430,8 +2463,23 @@ function estimateTokens(text) {
|
|
|
2430
2463
|
}
|
|
2431
2464
|
|
|
2432
2465
|
// src/llm/cursor-acp.ts
|
|
2433
|
-
var AGENT_BIN = "agent";
|
|
2434
2466
|
var IS_WINDOWS = process.platform === "win32";
|
|
2467
|
+
var _agentBin = null;
|
|
2468
|
+
function resolveAgentBin() {
|
|
2469
|
+
if (_agentBin !== null) return _agentBin;
|
|
2470
|
+
try {
|
|
2471
|
+
const whichCmd = IS_WINDOWS ? "where agent" : "which agent";
|
|
2472
|
+
const out = execSync5(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
2473
|
+
const p = out.split("\n")[0].trim();
|
|
2474
|
+
if (p) {
|
|
2475
|
+
_agentBin = p;
|
|
2476
|
+
return _agentBin;
|
|
2477
|
+
}
|
|
2478
|
+
} catch {
|
|
2479
|
+
}
|
|
2480
|
+
_agentBin = "agent";
|
|
2481
|
+
return _agentBin;
|
|
2482
|
+
}
|
|
2435
2483
|
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
2436
2484
|
var SIGKILL_DELAY_MS = 5e3;
|
|
2437
2485
|
var STDERR_MAX_BYTES = 10 * 1024;
|
|
@@ -2489,7 +2537,7 @@ var CursorAcpProvider = class {
|
|
|
2489
2537
|
const targetModel = model || this.defaultModel;
|
|
2490
2538
|
if (this.warmProcess && !this.warmProcess.killed && this.warmModel === targetModel) return;
|
|
2491
2539
|
const args = this.buildArgs(targetModel, false);
|
|
2492
|
-
this.warmProcess = spawn(
|
|
2540
|
+
this.warmProcess = spawn(resolveAgentBin(), args, {
|
|
2493
2541
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2494
2542
|
env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
|
|
2495
2543
|
...IS_WINDOWS && { shell: true }
|
|
@@ -2536,7 +2584,7 @@ var CursorAcpProvider = class {
|
|
|
2536
2584
|
return { child: warm, stderrChunks: stderrChunks2 };
|
|
2537
2585
|
}
|
|
2538
2586
|
const args = this.buildArgs(model, streaming);
|
|
2539
|
-
const child = spawn(
|
|
2587
|
+
const child = spawn(resolveAgentBin(), args, {
|
|
2540
2588
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2541
2589
|
env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
|
|
2542
2590
|
...IS_WINDOWS && { shell: true }
|
|
@@ -2710,9 +2758,9 @@ var CursorAcpProvider = class {
|
|
|
2710
2758
|
}
|
|
2711
2759
|
};
|
|
2712
2760
|
function isCursorAgentAvailable() {
|
|
2761
|
+
if (resolveAgentBin() !== "agent") return true;
|
|
2713
2762
|
try {
|
|
2714
|
-
|
|
2715
|
-
execSync5(cmd, { stdio: "ignore" });
|
|
2763
|
+
execSync5(IS_WINDOWS ? "where agent" : "which agent", { stdio: "ignore" });
|
|
2716
2764
|
return true;
|
|
2717
2765
|
} catch {
|
|
2718
2766
|
return false;
|
|
@@ -2720,7 +2768,7 @@ function isCursorAgentAvailable() {
|
|
|
2720
2768
|
}
|
|
2721
2769
|
function isCursorLoggedIn() {
|
|
2722
2770
|
try {
|
|
2723
|
-
const result =
|
|
2771
|
+
const result = execFileSync(resolveAgentBin(), ["status"], {
|
|
2724
2772
|
input: "",
|
|
2725
2773
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2726
2774
|
timeout: 5e3
|
|
@@ -2732,18 +2780,56 @@ function isCursorLoggedIn() {
|
|
|
2732
2780
|
}
|
|
2733
2781
|
|
|
2734
2782
|
// src/llm/claude-cli.ts
|
|
2735
|
-
import
|
|
2736
|
-
|
|
2783
|
+
import fs7 from "fs";
|
|
2784
|
+
import { spawn as spawn2, execSync as execSync6, execFileSync as execFileSync2 } from "child_process";
|
|
2737
2785
|
var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
2738
2786
|
var IS_WINDOWS2 = process.platform === "win32";
|
|
2787
|
+
function candidateClaudePaths() {
|
|
2788
|
+
if (IS_WINDOWS2) return [];
|
|
2789
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
2790
|
+
return [
|
|
2791
|
+
`${home}/.local/bin/claude`,
|
|
2792
|
+
// Claude Code default installer path
|
|
2793
|
+
"/usr/local/bin/claude",
|
|
2794
|
+
// Homebrew / manual install
|
|
2795
|
+
"/opt/homebrew/bin/claude"
|
|
2796
|
+
// Apple Silicon Homebrew
|
|
2797
|
+
].filter(Boolean);
|
|
2798
|
+
}
|
|
2799
|
+
var _claudeBin = null;
|
|
2800
|
+
function resolveClaudeBin() {
|
|
2801
|
+
if (_claudeBin !== null) return _claudeBin;
|
|
2802
|
+
try {
|
|
2803
|
+
const whichCmd = IS_WINDOWS2 ? "where claude" : "which claude";
|
|
2804
|
+
const out = execSync6(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
2805
|
+
const p = out.split("\n")[0].trim();
|
|
2806
|
+
if (p) {
|
|
2807
|
+
_claudeBin = p;
|
|
2808
|
+
return _claudeBin;
|
|
2809
|
+
}
|
|
2810
|
+
} catch {
|
|
2811
|
+
}
|
|
2812
|
+
for (const candidate of candidateClaudePaths()) {
|
|
2813
|
+
try {
|
|
2814
|
+
fs7.accessSync(candidate, fs7.constants.X_OK);
|
|
2815
|
+
_claudeBin = candidate;
|
|
2816
|
+
return _claudeBin;
|
|
2817
|
+
} catch {
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
_claudeBin = "claude";
|
|
2821
|
+
return _claudeBin;
|
|
2822
|
+
}
|
|
2739
2823
|
function spawnClaude(args) {
|
|
2740
|
-
const
|
|
2741
|
-
|
|
2824
|
+
const bin = resolveClaudeBin();
|
|
2825
|
+
const { CLAUDE_CODE_SIMPLE: _stripped, ...parentEnv } = process.env;
|
|
2826
|
+
const env = parentEnv;
|
|
2827
|
+
return IS_WINDOWS2 ? spawn2([bin, ...args].join(" "), {
|
|
2742
2828
|
cwd: process.cwd(),
|
|
2743
2829
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2744
2830
|
env,
|
|
2745
2831
|
shell: true
|
|
2746
|
-
}) : spawn2(
|
|
2832
|
+
}) : spawn2(bin, args, {
|
|
2747
2833
|
cwd: process.cwd(),
|
|
2748
2834
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2749
2835
|
env
|
|
@@ -2818,8 +2904,8 @@ var ClaudeCliProvider = class {
|
|
|
2818
2904
|
callbacks.onEnd({ stopReason: "end_turn" });
|
|
2819
2905
|
} else {
|
|
2820
2906
|
const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
|
|
2821
|
-
const friendly = parseSeatBasedError(stderr, code);
|
|
2822
2907
|
const stdout = Buffer.concat(chunks).toString("utf-8").trim();
|
|
2908
|
+
const friendly = parseSeatBasedError(stderr || stdout, code);
|
|
2823
2909
|
const base = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
|
|
2824
2910
|
const detail = friendly || stderr || (stdout ? stdout.slice(0, 200) : "");
|
|
2825
2911
|
callbacks.onError(new Error(detail ? `${base}. ${detail}` : base));
|
|
@@ -2879,7 +2965,7 @@ var ClaudeCliProvider = class {
|
|
|
2879
2965
|
resolve3(stdout);
|
|
2880
2966
|
} else {
|
|
2881
2967
|
const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
|
|
2882
|
-
const friendly = parseSeatBasedError(stderr, code);
|
|
2968
|
+
const friendly = parseSeatBasedError(stderr || stdout, code);
|
|
2883
2969
|
const base = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
|
|
2884
2970
|
const detail = friendly || stderr || (stdout ? stdout.slice(0, 200) : "");
|
|
2885
2971
|
reject(new Error(detail ? `${base}. ${detail}` : base));
|
|
@@ -2889,9 +2975,9 @@ var ClaudeCliProvider = class {
|
|
|
2889
2975
|
}
|
|
2890
2976
|
};
|
|
2891
2977
|
function isClaudeCliAvailable() {
|
|
2978
|
+
if (resolveClaudeBin() !== "claude") return true;
|
|
2892
2979
|
try {
|
|
2893
|
-
|
|
2894
|
-
execSync6(cmd, { stdio: "ignore" });
|
|
2980
|
+
execSync6(IS_WINDOWS2 ? "where claude" : "which claude", { stdio: "ignore" });
|
|
2895
2981
|
return true;
|
|
2896
2982
|
} catch {
|
|
2897
2983
|
return false;
|
|
@@ -2901,7 +2987,7 @@ var cachedLoggedIn = null;
|
|
|
2901
2987
|
function isClaudeCliLoggedIn() {
|
|
2902
2988
|
if (cachedLoggedIn !== null) return cachedLoggedIn;
|
|
2903
2989
|
try {
|
|
2904
|
-
const result =
|
|
2990
|
+
const result = execFileSync2(resolveClaudeBin(), ["auth", "status"], {
|
|
2905
2991
|
input: "",
|
|
2906
2992
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2907
2993
|
timeout: 5e3
|
|
@@ -3720,7 +3806,7 @@ async function detectProjectStack(fileTree, suffixCounts) {
|
|
|
3720
3806
|
init_config();
|
|
3721
3807
|
|
|
3722
3808
|
// src/fingerprint/cache.ts
|
|
3723
|
-
import
|
|
3809
|
+
import fs8 from "fs";
|
|
3724
3810
|
import path7 from "path";
|
|
3725
3811
|
import crypto from "crypto";
|
|
3726
3812
|
import { execSync as execSync7 } from "child_process";
|
|
@@ -3765,8 +3851,8 @@ function computeTreeSignature(fileTree, dir) {
|
|
|
3765
3851
|
function loadFingerprintCache(dir, fileTree) {
|
|
3766
3852
|
const cachePath = getCachePath(dir);
|
|
3767
3853
|
try {
|
|
3768
|
-
if (!
|
|
3769
|
-
const raw =
|
|
3854
|
+
if (!fs8.existsSync(cachePath)) return null;
|
|
3855
|
+
const raw = fs8.readFileSync(cachePath, "utf-8");
|
|
3770
3856
|
const cache = JSON.parse(raw);
|
|
3771
3857
|
if (cache.version !== CACHE_VERSION) return null;
|
|
3772
3858
|
const currentHead = getGitHead(dir);
|
|
@@ -3788,8 +3874,8 @@ function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks
|
|
|
3788
3874
|
const cachePath = getCachePath(dir);
|
|
3789
3875
|
try {
|
|
3790
3876
|
const cacheDir = path7.dirname(cachePath);
|
|
3791
|
-
if (!
|
|
3792
|
-
|
|
3877
|
+
if (!fs8.existsSync(cacheDir)) {
|
|
3878
|
+
fs8.mkdirSync(cacheDir, { recursive: true });
|
|
3793
3879
|
}
|
|
3794
3880
|
const cache = {
|
|
3795
3881
|
version: CACHE_VERSION,
|
|
@@ -3801,15 +3887,15 @@ function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks
|
|
|
3801
3887
|
tools,
|
|
3802
3888
|
workspaces
|
|
3803
3889
|
};
|
|
3804
|
-
|
|
3890
|
+
fs8.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
|
|
3805
3891
|
} catch {
|
|
3806
3892
|
}
|
|
3807
3893
|
}
|
|
3808
3894
|
function getDetectedWorkspaces(dir) {
|
|
3809
3895
|
const cachePath = getCachePath(dir);
|
|
3810
3896
|
try {
|
|
3811
|
-
if (!
|
|
3812
|
-
const raw =
|
|
3897
|
+
if (!fs8.existsSync(cachePath)) return [];
|
|
3898
|
+
const raw = fs8.readFileSync(cachePath, "utf-8");
|
|
3813
3899
|
const cache = JSON.parse(raw);
|
|
3814
3900
|
return cache.workspaces ?? [];
|
|
3815
3901
|
} catch {
|
|
@@ -3862,8 +3948,8 @@ async function collectFingerprint(dir) {
|
|
|
3862
3948
|
function readPackageName(dir) {
|
|
3863
3949
|
try {
|
|
3864
3950
|
const pkgPath = path8.join(dir, "package.json");
|
|
3865
|
-
if (!
|
|
3866
|
-
const pkg3 = JSON.parse(
|
|
3951
|
+
if (!fs9.existsSync(pkgPath)) return void 0;
|
|
3952
|
+
const pkg3 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
3867
3953
|
return pkg3.name;
|
|
3868
3954
|
} catch {
|
|
3869
3955
|
return void 0;
|
|
@@ -3893,23 +3979,23 @@ async function enrichWithLLM(fingerprint) {
|
|
|
3893
3979
|
}
|
|
3894
3980
|
|
|
3895
3981
|
// src/scanner/index.ts
|
|
3896
|
-
import
|
|
3982
|
+
import fs10 from "fs";
|
|
3897
3983
|
import path9 from "path";
|
|
3898
3984
|
import crypto2 from "crypto";
|
|
3899
3985
|
import os4 from "os";
|
|
3900
3986
|
function detectPlatforms() {
|
|
3901
3987
|
const home = os4.homedir();
|
|
3902
3988
|
return {
|
|
3903
|
-
claude:
|
|
3904
|
-
cursor:
|
|
3905
|
-
codex:
|
|
3906
|
-
opencode:
|
|
3989
|
+
claude: fs10.existsSync(path9.join(home, ".claude")),
|
|
3990
|
+
cursor: fs10.existsSync(getCursorConfigDir()),
|
|
3991
|
+
codex: fs10.existsSync(path9.join(home, ".codex")),
|
|
3992
|
+
opencode: fs10.existsSync(path9.join(home, ".config", "opencode"))
|
|
3907
3993
|
};
|
|
3908
3994
|
}
|
|
3909
3995
|
function scanLocalState(dir) {
|
|
3910
3996
|
const items = [];
|
|
3911
3997
|
const claudeMdPath = path9.join(dir, "CLAUDE.md");
|
|
3912
|
-
if (
|
|
3998
|
+
if (fs10.existsSync(claudeMdPath)) {
|
|
3913
3999
|
items.push({
|
|
3914
4000
|
type: "rule",
|
|
3915
4001
|
platform: "claude",
|
|
@@ -3919,8 +4005,8 @@ function scanLocalState(dir) {
|
|
|
3919
4005
|
});
|
|
3920
4006
|
}
|
|
3921
4007
|
const skillsDir = path9.join(dir, ".claude", "skills");
|
|
3922
|
-
if (
|
|
3923
|
-
for (const file of
|
|
4008
|
+
if (fs10.existsSync(skillsDir)) {
|
|
4009
|
+
for (const file of fs10.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
3924
4010
|
const filePath = path9.join(skillsDir, file);
|
|
3925
4011
|
items.push({
|
|
3926
4012
|
type: "skill",
|
|
@@ -3932,9 +4018,9 @@ function scanLocalState(dir) {
|
|
|
3932
4018
|
}
|
|
3933
4019
|
}
|
|
3934
4020
|
const mcpJsonPath = path9.join(dir, ".mcp.json");
|
|
3935
|
-
if (
|
|
4021
|
+
if (fs10.existsSync(mcpJsonPath)) {
|
|
3936
4022
|
try {
|
|
3937
|
-
const mcpJson = JSON.parse(
|
|
4023
|
+
const mcpJson = JSON.parse(fs10.readFileSync(mcpJsonPath, "utf-8"));
|
|
3938
4024
|
if (mcpJson.mcpServers) {
|
|
3939
4025
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
3940
4026
|
items.push({
|
|
@@ -3951,7 +4037,7 @@ function scanLocalState(dir) {
|
|
|
3951
4037
|
}
|
|
3952
4038
|
}
|
|
3953
4039
|
const agentsMdPath = path9.join(dir, "AGENTS.md");
|
|
3954
|
-
if (
|
|
4040
|
+
if (fs10.existsSync(agentsMdPath)) {
|
|
3955
4041
|
items.push({
|
|
3956
4042
|
type: "rule",
|
|
3957
4043
|
platform: "codex",
|
|
@@ -3961,11 +4047,11 @@ function scanLocalState(dir) {
|
|
|
3961
4047
|
});
|
|
3962
4048
|
}
|
|
3963
4049
|
const codexSkillsDir = path9.join(dir, ".agents", "skills");
|
|
3964
|
-
if (
|
|
4050
|
+
if (fs10.existsSync(codexSkillsDir)) {
|
|
3965
4051
|
try {
|
|
3966
|
-
for (const name of
|
|
4052
|
+
for (const name of fs10.readdirSync(codexSkillsDir)) {
|
|
3967
4053
|
const skillFile = path9.join(codexSkillsDir, name, "SKILL.md");
|
|
3968
|
-
if (
|
|
4054
|
+
if (fs10.existsSync(skillFile)) {
|
|
3969
4055
|
items.push({
|
|
3970
4056
|
type: "skill",
|
|
3971
4057
|
platform: "codex",
|
|
@@ -3980,11 +4066,11 @@ function scanLocalState(dir) {
|
|
|
3980
4066
|
}
|
|
3981
4067
|
}
|
|
3982
4068
|
const opencodeSkillsDir = path9.join(dir, ".opencode", "skills");
|
|
3983
|
-
if (
|
|
4069
|
+
if (fs10.existsSync(opencodeSkillsDir)) {
|
|
3984
4070
|
try {
|
|
3985
|
-
for (const name of
|
|
4071
|
+
for (const name of fs10.readdirSync(opencodeSkillsDir)) {
|
|
3986
4072
|
const skillFile = path9.join(opencodeSkillsDir, name, "SKILL.md");
|
|
3987
|
-
if (
|
|
4073
|
+
if (fs10.existsSync(skillFile)) {
|
|
3988
4074
|
items.push({
|
|
3989
4075
|
type: "skill",
|
|
3990
4076
|
platform: "opencode",
|
|
@@ -3999,7 +4085,7 @@ function scanLocalState(dir) {
|
|
|
3999
4085
|
}
|
|
4000
4086
|
}
|
|
4001
4087
|
const cursorrulesPath = path9.join(dir, ".cursorrules");
|
|
4002
|
-
if (
|
|
4088
|
+
if (fs10.existsSync(cursorrulesPath)) {
|
|
4003
4089
|
items.push({
|
|
4004
4090
|
type: "rule",
|
|
4005
4091
|
platform: "cursor",
|
|
@@ -4009,8 +4095,8 @@ function scanLocalState(dir) {
|
|
|
4009
4095
|
});
|
|
4010
4096
|
}
|
|
4011
4097
|
const cursorRulesDir = path9.join(dir, ".cursor", "rules");
|
|
4012
|
-
if (
|
|
4013
|
-
for (const file of
|
|
4098
|
+
if (fs10.existsSync(cursorRulesDir)) {
|
|
4099
|
+
for (const file of fs10.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
4014
4100
|
const filePath = path9.join(cursorRulesDir, file);
|
|
4015
4101
|
items.push({
|
|
4016
4102
|
type: "rule",
|
|
@@ -4022,11 +4108,11 @@ function scanLocalState(dir) {
|
|
|
4022
4108
|
}
|
|
4023
4109
|
}
|
|
4024
4110
|
const cursorSkillsDir = path9.join(dir, ".cursor", "skills");
|
|
4025
|
-
if (
|
|
4111
|
+
if (fs10.existsSync(cursorSkillsDir)) {
|
|
4026
4112
|
try {
|
|
4027
|
-
for (const name of
|
|
4113
|
+
for (const name of fs10.readdirSync(cursorSkillsDir)) {
|
|
4028
4114
|
const skillFile = path9.join(cursorSkillsDir, name, "SKILL.md");
|
|
4029
|
-
if (
|
|
4115
|
+
if (fs10.existsSync(skillFile)) {
|
|
4030
4116
|
items.push({
|
|
4031
4117
|
type: "skill",
|
|
4032
4118
|
platform: "cursor",
|
|
@@ -4041,9 +4127,9 @@ function scanLocalState(dir) {
|
|
|
4041
4127
|
}
|
|
4042
4128
|
}
|
|
4043
4129
|
const cursorMcpPath = path9.join(dir, ".cursor", "mcp.json");
|
|
4044
|
-
if (
|
|
4130
|
+
if (fs10.existsSync(cursorMcpPath)) {
|
|
4045
4131
|
try {
|
|
4046
|
-
const mcpJson = JSON.parse(
|
|
4132
|
+
const mcpJson = JSON.parse(fs10.readFileSync(cursorMcpPath, "utf-8"));
|
|
4047
4133
|
if (mcpJson.mcpServers) {
|
|
4048
4134
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
4049
4135
|
items.push({
|
|
@@ -4062,7 +4148,7 @@ function scanLocalState(dir) {
|
|
|
4062
4148
|
return items;
|
|
4063
4149
|
}
|
|
4064
4150
|
function hashFile(filePath) {
|
|
4065
|
-
const text =
|
|
4151
|
+
const text = fs10.readFileSync(filePath, "utf-8");
|
|
4066
4152
|
return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
4067
4153
|
}
|
|
4068
4154
|
function hashJson(obj) {
|
|
@@ -4085,7 +4171,7 @@ function getCursorConfigDir() {
|
|
|
4085
4171
|
|
|
4086
4172
|
// src/lib/hooks.ts
|
|
4087
4173
|
init_resolve_caliber();
|
|
4088
|
-
import
|
|
4174
|
+
import fs11 from "fs";
|
|
4089
4175
|
import path10 from "path";
|
|
4090
4176
|
import { execSync as execSync8 } from "child_process";
|
|
4091
4177
|
var SETTINGS_PATH = path10.join(".claude", "settings.json");
|
|
@@ -4095,17 +4181,17 @@ function getHookCommand() {
|
|
|
4095
4181
|
return `${resolveCaliber()} ${REFRESH_TAIL}`;
|
|
4096
4182
|
}
|
|
4097
4183
|
function readSettings() {
|
|
4098
|
-
if (!
|
|
4184
|
+
if (!fs11.existsSync(SETTINGS_PATH)) return {};
|
|
4099
4185
|
try {
|
|
4100
|
-
return JSON.parse(
|
|
4186
|
+
return JSON.parse(fs11.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
4101
4187
|
} catch {
|
|
4102
4188
|
return {};
|
|
4103
4189
|
}
|
|
4104
4190
|
}
|
|
4105
4191
|
function writeSettings(settings) {
|
|
4106
4192
|
const dir = path10.dirname(SETTINGS_PATH);
|
|
4107
|
-
if (!
|
|
4108
|
-
|
|
4193
|
+
if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
|
|
4194
|
+
fs11.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
4109
4195
|
}
|
|
4110
4196
|
function findHookIndex(sessionEnd) {
|
|
4111
4197
|
return sessionEnd.findIndex(
|
|
@@ -4153,7 +4239,8 @@ function removeHook() {
|
|
|
4153
4239
|
return { removed: true, notFound: false };
|
|
4154
4240
|
}
|
|
4155
4241
|
function createScriptHook(config) {
|
|
4156
|
-
const { eventName, scriptPath,
|
|
4242
|
+
const { eventName, scriptPath, description } = config;
|
|
4243
|
+
const getContent = () => typeof config.scriptContent === "function" ? config.scriptContent() : config.scriptContent;
|
|
4157
4244
|
const hasHook = (matchers) => matchers.some((entry) => entry.hooks?.some((h) => h.description === description));
|
|
4158
4245
|
function isInstalled() {
|
|
4159
4246
|
const settings = readSettings();
|
|
@@ -4168,9 +4255,9 @@ function createScriptHook(config) {
|
|
|
4168
4255
|
return { installed: false, alreadyInstalled: true };
|
|
4169
4256
|
}
|
|
4170
4257
|
const scriptDir = path10.dirname(scriptPath);
|
|
4171
|
-
if (!
|
|
4172
|
-
|
|
4173
|
-
|
|
4258
|
+
if (!fs11.existsSync(scriptDir)) fs11.mkdirSync(scriptDir, { recursive: true });
|
|
4259
|
+
fs11.writeFileSync(scriptPath, getContent());
|
|
4260
|
+
fs11.chmodSync(scriptPath, 493);
|
|
4174
4261
|
if (!Array.isArray(settings.hooks[eventName])) {
|
|
4175
4262
|
settings.hooks[eventName] = [];
|
|
4176
4263
|
}
|
|
@@ -4194,7 +4281,7 @@ function createScriptHook(config) {
|
|
|
4194
4281
|
if (settings.hooks && Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
4195
4282
|
writeSettings(settings);
|
|
4196
4283
|
try {
|
|
4197
|
-
|
|
4284
|
+
fs11.unlinkSync(scriptPath);
|
|
4198
4285
|
} catch {
|
|
4199
4286
|
}
|
|
4200
4287
|
return { removed: true, notFound: false };
|
|
@@ -4221,7 +4308,9 @@ var stopHook = createScriptHook({
|
|
|
4221
4308
|
});
|
|
4222
4309
|
var installStopHook = stopHook.install;
|
|
4223
4310
|
var removeStopHook = stopHook.remove;
|
|
4224
|
-
|
|
4311
|
+
function getFreshnessScript() {
|
|
4312
|
+
const bin = resolveCaliber();
|
|
4313
|
+
return `#!/bin/sh
|
|
4225
4314
|
STATE_FILE=".caliber/.caliber-state.json"
|
|
4226
4315
|
[ ! -f "$STATE_FILE" ] && exit 0
|
|
4227
4316
|
LAST_SHA=$(grep -o '"lastRefreshSha":"[^"]*"' "$STATE_FILE" 2>/dev/null | cut -d'"' -f4)
|
|
@@ -4230,13 +4319,14 @@ CURRENT_SHA=$(git rev-parse HEAD 2>/dev/null)
|
|
|
4230
4319
|
[ "$LAST_SHA" = "$CURRENT_SHA" ] && exit 0
|
|
4231
4320
|
COMMITS_BEHIND=$(git rev-list --count "$LAST_SHA".."$CURRENT_SHA" 2>/dev/null || echo 0)
|
|
4232
4321
|
if [ "$COMMITS_BEHIND" -gt 15 ]; then
|
|
4233
|
-
printf '{"systemMessage":"Caliber: agent configs are %s commits behind. Run
|
|
4322
|
+
printf '{"systemMessage":"Caliber: agent configs are %s commits behind. Run ${bin} refresh to sync."}' "$COMMITS_BEHIND"
|
|
4234
4323
|
fi
|
|
4235
4324
|
`;
|
|
4325
|
+
}
|
|
4236
4326
|
var sessionStartHook = createScriptHook({
|
|
4237
4327
|
eventName: "SessionStart",
|
|
4238
4328
|
scriptPath: path10.join(".claude", "hooks", "caliber-session-freshness.sh"),
|
|
4239
|
-
scriptContent:
|
|
4329
|
+
scriptContent: getFreshnessScript,
|
|
4240
4330
|
description: "Caliber: check config freshness on session start"
|
|
4241
4331
|
});
|
|
4242
4332
|
var isSessionStartHookInstalled = sessionStartHook.isInstalled;
|
|
@@ -4245,7 +4335,7 @@ var removeSessionStartHook = sessionStartHook.remove;
|
|
|
4245
4335
|
var notificationHook = createScriptHook({
|
|
4246
4336
|
eventName: "Notification",
|
|
4247
4337
|
scriptPath: path10.join(".claude", "hooks", "caliber-freshness-notify.sh"),
|
|
4248
|
-
scriptContent:
|
|
4338
|
+
scriptContent: getFreshnessScript,
|
|
4249
4339
|
description: "Caliber: warn when agent configs are stale"
|
|
4250
4340
|
});
|
|
4251
4341
|
var isNotificationHookInstalled = notificationHook.isInstalled;
|
|
@@ -4254,10 +4344,28 @@ var removeNotificationHook = notificationHook.remove;
|
|
|
4254
4344
|
var PRECOMMIT_START = "# caliber:pre-commit:start";
|
|
4255
4345
|
var PRECOMMIT_END = "# caliber:pre-commit:end";
|
|
4256
4346
|
function getPrecommitBlock() {
|
|
4257
|
-
const
|
|
4347
|
+
const cmd = resolveCaliber();
|
|
4258
4348
|
const npx = isNpxResolution();
|
|
4259
|
-
|
|
4260
|
-
|
|
4349
|
+
let guard;
|
|
4350
|
+
let invoke;
|
|
4351
|
+
if (npx) {
|
|
4352
|
+
const npxBin = cmd.split(" ")[0];
|
|
4353
|
+
if (npxBin.startsWith("/")) {
|
|
4354
|
+
guard = `[ -x "${npxBin}" ]`;
|
|
4355
|
+
const npxArgs = cmd.slice(npxBin.length);
|
|
4356
|
+
invoke = `"${npxBin}"${npxArgs}`;
|
|
4357
|
+
} else {
|
|
4358
|
+
guard = "command -v npx >/dev/null 2>&1";
|
|
4359
|
+
invoke = cmd;
|
|
4360
|
+
}
|
|
4361
|
+
} else {
|
|
4362
|
+
if (cmd.startsWith("/")) {
|
|
4363
|
+
guard = `[ -x "${cmd}" ]`;
|
|
4364
|
+
} else {
|
|
4365
|
+
guard = `[ -x "${cmd}" ] || command -v "${cmd}" >/dev/null 2>&1`;
|
|
4366
|
+
}
|
|
4367
|
+
invoke = `"${cmd}"`;
|
|
4368
|
+
}
|
|
4261
4369
|
return `${PRECOMMIT_START}
|
|
4262
4370
|
if ${guard}; then
|
|
4263
4371
|
mkdir -p .caliber
|
|
@@ -4285,8 +4393,8 @@ function getPreCommitPath() {
|
|
|
4285
4393
|
}
|
|
4286
4394
|
function isPreCommitHookInstalled() {
|
|
4287
4395
|
const hookPath = getPreCommitPath();
|
|
4288
|
-
if (!hookPath || !
|
|
4289
|
-
const content =
|
|
4396
|
+
if (!hookPath || !fs11.existsSync(hookPath)) return false;
|
|
4397
|
+
const content = fs11.readFileSync(hookPath, "utf-8");
|
|
4290
4398
|
return content.includes(PRECOMMIT_START);
|
|
4291
4399
|
}
|
|
4292
4400
|
function installPreCommitHook() {
|
|
@@ -4296,45 +4404,45 @@ function installPreCommitHook() {
|
|
|
4296
4404
|
const hookPath = getPreCommitPath();
|
|
4297
4405
|
if (!hookPath) return { installed: false, alreadyInstalled: false };
|
|
4298
4406
|
const hooksDir = path10.dirname(hookPath);
|
|
4299
|
-
if (!
|
|
4407
|
+
if (!fs11.existsSync(hooksDir)) fs11.mkdirSync(hooksDir, { recursive: true });
|
|
4300
4408
|
let content = "";
|
|
4301
|
-
if (
|
|
4302
|
-
content =
|
|
4409
|
+
if (fs11.existsSync(hookPath)) {
|
|
4410
|
+
content = fs11.readFileSync(hookPath, "utf-8");
|
|
4303
4411
|
if (!content.endsWith("\n")) content += "\n";
|
|
4304
4412
|
content += "\n" + getPrecommitBlock() + "\n";
|
|
4305
4413
|
} else {
|
|
4306
4414
|
content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
|
|
4307
4415
|
}
|
|
4308
|
-
|
|
4309
|
-
|
|
4416
|
+
fs11.writeFileSync(hookPath, content);
|
|
4417
|
+
fs11.chmodSync(hookPath, 493);
|
|
4310
4418
|
return { installed: true, alreadyInstalled: false };
|
|
4311
4419
|
}
|
|
4312
4420
|
function removePreCommitHook() {
|
|
4313
4421
|
const hookPath = getPreCommitPath();
|
|
4314
|
-
if (!hookPath || !
|
|
4422
|
+
if (!hookPath || !fs11.existsSync(hookPath)) {
|
|
4315
4423
|
return { removed: false, notFound: true };
|
|
4316
4424
|
}
|
|
4317
|
-
let content =
|
|
4425
|
+
let content = fs11.readFileSync(hookPath, "utf-8");
|
|
4318
4426
|
if (!content.includes(PRECOMMIT_START)) {
|
|
4319
4427
|
return { removed: false, notFound: true };
|
|
4320
4428
|
}
|
|
4321
4429
|
const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
|
|
4322
4430
|
content = content.replace(regex, "\n");
|
|
4323
4431
|
if (content.trim() === "#!/bin/sh" || content.trim() === "") {
|
|
4324
|
-
|
|
4432
|
+
fs11.unlinkSync(hookPath);
|
|
4325
4433
|
} else {
|
|
4326
|
-
|
|
4434
|
+
fs11.writeFileSync(hookPath, content);
|
|
4327
4435
|
}
|
|
4328
4436
|
return { removed: true, notFound: false };
|
|
4329
4437
|
}
|
|
4330
4438
|
|
|
4331
4439
|
// src/fingerprint/sources.ts
|
|
4332
|
-
import
|
|
4440
|
+
import fs12 from "fs";
|
|
4333
4441
|
import path11 from "path";
|
|
4334
4442
|
|
|
4335
4443
|
// src/scoring/utils.ts
|
|
4336
4444
|
import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
|
|
4337
|
-
import { execFileSync } from "child_process";
|
|
4445
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
4338
4446
|
import { join, relative } from "path";
|
|
4339
4447
|
function readFileOrNull(filePath) {
|
|
4340
4448
|
try {
|
|
@@ -4384,7 +4492,7 @@ var IGNORED_FILES = /* @__PURE__ */ new Set([
|
|
|
4384
4492
|
]);
|
|
4385
4493
|
function isGitRepo2(dir) {
|
|
4386
4494
|
try {
|
|
4387
|
-
|
|
4495
|
+
execFileSync3("git", ["rev-parse", "--git-dir"], {
|
|
4388
4496
|
cwd: dir,
|
|
4389
4497
|
encoding: "utf-8",
|
|
4390
4498
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4397,7 +4505,7 @@ function isGitRepo2(dir) {
|
|
|
4397
4505
|
function checkGitIgnored(dir, paths) {
|
|
4398
4506
|
if (paths.length === 0) return /* @__PURE__ */ new Set();
|
|
4399
4507
|
try {
|
|
4400
|
-
const result =
|
|
4508
|
+
const result = execFileSync3("git", ["check-ignore", ...paths], {
|
|
4401
4509
|
cwd: dir,
|
|
4402
4510
|
encoding: "utf-8",
|
|
4403
4511
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4678,15 +4786,15 @@ function loadSourcesConfig(dir) {
|
|
|
4678
4786
|
}
|
|
4679
4787
|
function writeSourcesConfig(dir, sources2) {
|
|
4680
4788
|
const configDir = path11.join(dir, ".caliber");
|
|
4681
|
-
if (!
|
|
4682
|
-
|
|
4789
|
+
if (!fs12.existsSync(configDir)) {
|
|
4790
|
+
fs12.mkdirSync(configDir, { recursive: true });
|
|
4683
4791
|
}
|
|
4684
4792
|
const configPath = path11.join(configDir, "sources.json");
|
|
4685
|
-
|
|
4793
|
+
fs12.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
|
|
4686
4794
|
}
|
|
4687
4795
|
function detectSourceType(absPath) {
|
|
4688
4796
|
try {
|
|
4689
|
-
return
|
|
4797
|
+
return fs12.statSync(absPath).isDirectory() ? "repo" : "file";
|
|
4690
4798
|
} catch {
|
|
4691
4799
|
return "file";
|
|
4692
4800
|
}
|
|
@@ -4730,7 +4838,7 @@ function resolveAllSources(dir, cliSources, workspaces) {
|
|
|
4730
4838
|
for (const [absPath, resolved] of seen) {
|
|
4731
4839
|
let stat;
|
|
4732
4840
|
try {
|
|
4733
|
-
stat =
|
|
4841
|
+
stat = fs12.statSync(absPath);
|
|
4734
4842
|
} catch {
|
|
4735
4843
|
console.warn(`Source ${resolved.config.path || absPath} not found, skipping`);
|
|
4736
4844
|
continue;
|
|
@@ -4785,7 +4893,7 @@ function collectRepoSummary(resolved, _projectDir) {
|
|
|
4785
4893
|
let topLevelDirs;
|
|
4786
4894
|
let keyFiles;
|
|
4787
4895
|
try {
|
|
4788
|
-
const entries =
|
|
4896
|
+
const entries = fs12.readdirSync(absPath, { withFileTypes: true });
|
|
4789
4897
|
topLevelDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name).slice(0, 20);
|
|
4790
4898
|
keyFiles = entries.filter((e) => e.isFile() && !e.name.startsWith(".")).map((e) => e.name).slice(0, 15);
|
|
4791
4899
|
} catch {
|
|
@@ -5646,32 +5754,32 @@ ${f.content}
|
|
|
5646
5754
|
}
|
|
5647
5755
|
|
|
5648
5756
|
// src/writers/index.ts
|
|
5649
|
-
import
|
|
5757
|
+
import fs21 from "fs";
|
|
5650
5758
|
|
|
5651
5759
|
// src/writers/claude/index.ts
|
|
5652
5760
|
init_pre_commit_block();
|
|
5653
|
-
import
|
|
5761
|
+
import fs13 from "fs";
|
|
5654
5762
|
import path12 from "path";
|
|
5655
5763
|
function writeClaudeConfig(config) {
|
|
5656
5764
|
const written = [];
|
|
5657
|
-
|
|
5765
|
+
fs13.writeFileSync(
|
|
5658
5766
|
"CLAUDE.md",
|
|
5659
5767
|
appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(config.claudeMd)))
|
|
5660
5768
|
);
|
|
5661
5769
|
written.push("CLAUDE.md");
|
|
5662
5770
|
if (config.rules?.length) {
|
|
5663
5771
|
const rulesDir = path12.join(".claude", "rules");
|
|
5664
|
-
if (!
|
|
5772
|
+
if (!fs13.existsSync(rulesDir)) fs13.mkdirSync(rulesDir, { recursive: true });
|
|
5665
5773
|
for (const rule of config.rules) {
|
|
5666
5774
|
const rulePath = path12.join(rulesDir, rule.filename);
|
|
5667
|
-
|
|
5775
|
+
fs13.writeFileSync(rulePath, rule.content);
|
|
5668
5776
|
written.push(rulePath);
|
|
5669
5777
|
}
|
|
5670
5778
|
}
|
|
5671
5779
|
if (config.skills?.length) {
|
|
5672
5780
|
for (const skill of config.skills) {
|
|
5673
5781
|
const skillDir = path12.join(".claude", "skills", skill.name);
|
|
5674
|
-
if (!
|
|
5782
|
+
if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
|
|
5675
5783
|
const skillPath = path12.join(skillDir, "SKILL.md");
|
|
5676
5784
|
const frontmatterLines = ["---", `name: ${skill.name}`, `description: ${skill.description}`];
|
|
5677
5785
|
if (skill.paths?.length) {
|
|
@@ -5682,21 +5790,21 @@ function writeClaudeConfig(config) {
|
|
|
5682
5790
|
}
|
|
5683
5791
|
frontmatterLines.push("---", "");
|
|
5684
5792
|
const frontmatter = frontmatterLines.join("\n");
|
|
5685
|
-
|
|
5793
|
+
fs13.writeFileSync(skillPath, frontmatter + skill.content);
|
|
5686
5794
|
written.push(skillPath);
|
|
5687
5795
|
}
|
|
5688
5796
|
}
|
|
5689
5797
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
5690
5798
|
let existingServers = {};
|
|
5691
5799
|
try {
|
|
5692
|
-
if (
|
|
5693
|
-
const existing = JSON.parse(
|
|
5800
|
+
if (fs13.existsSync(".mcp.json")) {
|
|
5801
|
+
const existing = JSON.parse(fs13.readFileSync(".mcp.json", "utf-8"));
|
|
5694
5802
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
5695
5803
|
}
|
|
5696
5804
|
} catch {
|
|
5697
5805
|
}
|
|
5698
5806
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
5699
|
-
|
|
5807
|
+
fs13.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
5700
5808
|
written.push(".mcp.json");
|
|
5701
5809
|
}
|
|
5702
5810
|
return written;
|
|
@@ -5704,12 +5812,12 @@ function writeClaudeConfig(config) {
|
|
|
5704
5812
|
|
|
5705
5813
|
// src/writers/cursor/index.ts
|
|
5706
5814
|
init_pre_commit_block();
|
|
5707
|
-
import
|
|
5815
|
+
import fs14 from "fs";
|
|
5708
5816
|
import path13 from "path";
|
|
5709
5817
|
function writeCursorConfig(config) {
|
|
5710
5818
|
const written = [];
|
|
5711
5819
|
if (config.cursorrules) {
|
|
5712
|
-
|
|
5820
|
+
fs14.writeFileSync(".cursorrules", config.cursorrules);
|
|
5713
5821
|
written.push(".cursorrules");
|
|
5714
5822
|
}
|
|
5715
5823
|
const preCommitRule = getCursorPreCommitRule();
|
|
@@ -5717,16 +5825,16 @@ function writeCursorConfig(config) {
|
|
|
5717
5825
|
const syncRule = getCursorSyncRule();
|
|
5718
5826
|
const allRules = [...config.rules || [], preCommitRule, learningsRule, syncRule];
|
|
5719
5827
|
const rulesDir = path13.join(".cursor", "rules");
|
|
5720
|
-
if (!
|
|
5828
|
+
if (!fs14.existsSync(rulesDir)) fs14.mkdirSync(rulesDir, { recursive: true });
|
|
5721
5829
|
for (const rule of allRules) {
|
|
5722
5830
|
const rulePath = path13.join(rulesDir, rule.filename);
|
|
5723
|
-
|
|
5831
|
+
fs14.writeFileSync(rulePath, rule.content);
|
|
5724
5832
|
written.push(rulePath);
|
|
5725
5833
|
}
|
|
5726
5834
|
if (config.skills?.length) {
|
|
5727
5835
|
for (const skill of config.skills) {
|
|
5728
5836
|
const skillDir = path13.join(".cursor", "skills", skill.name);
|
|
5729
|
-
if (!
|
|
5837
|
+
if (!fs14.existsSync(skillDir)) fs14.mkdirSync(skillDir, { recursive: true });
|
|
5730
5838
|
const skillPath = path13.join(skillDir, "SKILL.md");
|
|
5731
5839
|
const frontmatter = [
|
|
5732
5840
|
"---",
|
|
@@ -5735,24 +5843,24 @@ function writeCursorConfig(config) {
|
|
|
5735
5843
|
"---",
|
|
5736
5844
|
""
|
|
5737
5845
|
].join("\n");
|
|
5738
|
-
|
|
5846
|
+
fs14.writeFileSync(skillPath, frontmatter + skill.content);
|
|
5739
5847
|
written.push(skillPath);
|
|
5740
5848
|
}
|
|
5741
5849
|
}
|
|
5742
5850
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
5743
5851
|
const cursorDir = ".cursor";
|
|
5744
|
-
if (!
|
|
5852
|
+
if (!fs14.existsSync(cursorDir)) fs14.mkdirSync(cursorDir, { recursive: true });
|
|
5745
5853
|
const mcpPath = path13.join(cursorDir, "mcp.json");
|
|
5746
5854
|
let existingServers = {};
|
|
5747
5855
|
try {
|
|
5748
|
-
if (
|
|
5749
|
-
const existing = JSON.parse(
|
|
5856
|
+
if (fs14.existsSync(mcpPath)) {
|
|
5857
|
+
const existing = JSON.parse(fs14.readFileSync(mcpPath, "utf-8"));
|
|
5750
5858
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
5751
5859
|
}
|
|
5752
5860
|
} catch {
|
|
5753
5861
|
}
|
|
5754
5862
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
5755
|
-
|
|
5863
|
+
fs14.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
5756
5864
|
written.push(mcpPath);
|
|
5757
5865
|
}
|
|
5758
5866
|
return written;
|
|
@@ -5760,11 +5868,11 @@ function writeCursorConfig(config) {
|
|
|
5760
5868
|
|
|
5761
5869
|
// src/writers/codex/index.ts
|
|
5762
5870
|
init_pre_commit_block();
|
|
5763
|
-
import
|
|
5871
|
+
import fs15 from "fs";
|
|
5764
5872
|
import path14 from "path";
|
|
5765
5873
|
function writeCodexConfig(config) {
|
|
5766
5874
|
const written = [];
|
|
5767
|
-
|
|
5875
|
+
fs15.writeFileSync(
|
|
5768
5876
|
"AGENTS.md",
|
|
5769
5877
|
appendLearningsBlock(appendPreCommitBlock(config.agentsMd, "codex"))
|
|
5770
5878
|
);
|
|
@@ -5772,7 +5880,7 @@ function writeCodexConfig(config) {
|
|
|
5772
5880
|
if (config.skills?.length) {
|
|
5773
5881
|
for (const skill of config.skills) {
|
|
5774
5882
|
const skillDir = path14.join(".agents", "skills", skill.name);
|
|
5775
|
-
if (!
|
|
5883
|
+
if (!fs15.existsSync(skillDir)) fs15.mkdirSync(skillDir, { recursive: true });
|
|
5776
5884
|
const skillPath = path14.join(skillDir, "SKILL.md");
|
|
5777
5885
|
const frontmatter = [
|
|
5778
5886
|
"---",
|
|
@@ -5781,7 +5889,7 @@ function writeCodexConfig(config) {
|
|
|
5781
5889
|
"---",
|
|
5782
5890
|
""
|
|
5783
5891
|
].join("\n");
|
|
5784
|
-
|
|
5892
|
+
fs15.writeFileSync(skillPath, frontmatter + skill.content);
|
|
5785
5893
|
written.push(skillPath);
|
|
5786
5894
|
}
|
|
5787
5895
|
}
|
|
@@ -5790,13 +5898,13 @@ function writeCodexConfig(config) {
|
|
|
5790
5898
|
|
|
5791
5899
|
// src/writers/github-copilot/index.ts
|
|
5792
5900
|
init_pre_commit_block();
|
|
5793
|
-
import
|
|
5901
|
+
import fs16 from "fs";
|
|
5794
5902
|
import path15 from "path";
|
|
5795
5903
|
function writeGithubCopilotConfig(config) {
|
|
5796
5904
|
const written = [];
|
|
5797
5905
|
if (config.instructions) {
|
|
5798
|
-
|
|
5799
|
-
|
|
5906
|
+
fs16.mkdirSync(".github", { recursive: true });
|
|
5907
|
+
fs16.writeFileSync(
|
|
5800
5908
|
path15.join(".github", "copilot-instructions.md"),
|
|
5801
5909
|
appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(config.instructions, "copilot")))
|
|
5802
5910
|
);
|
|
@@ -5804,9 +5912,9 @@ function writeGithubCopilotConfig(config) {
|
|
|
5804
5912
|
}
|
|
5805
5913
|
if (config.instructionFiles?.length) {
|
|
5806
5914
|
const instructionsDir = path15.join(".github", "instructions");
|
|
5807
|
-
|
|
5915
|
+
fs16.mkdirSync(instructionsDir, { recursive: true });
|
|
5808
5916
|
for (const file of config.instructionFiles) {
|
|
5809
|
-
|
|
5917
|
+
fs16.writeFileSync(path15.join(instructionsDir, file.filename), file.content);
|
|
5810
5918
|
written.push(`.github/instructions/${file.filename}`);
|
|
5811
5919
|
}
|
|
5812
5920
|
}
|
|
@@ -5816,12 +5924,12 @@ function writeGithubCopilotConfig(config) {
|
|
|
5816
5924
|
// src/writers/opencode/index.ts
|
|
5817
5925
|
init_pre_commit_block();
|
|
5818
5926
|
init_builtin_skills();
|
|
5819
|
-
import
|
|
5927
|
+
import fs18 from "fs";
|
|
5820
5928
|
import path17 from "path";
|
|
5821
5929
|
function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
|
|
5822
5930
|
const written = [];
|
|
5823
5931
|
if (!agentsMdAlreadyWritten) {
|
|
5824
|
-
|
|
5932
|
+
fs18.writeFileSync(
|
|
5825
5933
|
"AGENTS.md",
|
|
5826
5934
|
appendLearningsBlock(appendPreCommitBlock(config.agentsMd, "codex"))
|
|
5827
5935
|
);
|
|
@@ -5830,9 +5938,9 @@ function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
|
|
|
5830
5938
|
if (config.skills?.length) {
|
|
5831
5939
|
for (const skill of config.skills) {
|
|
5832
5940
|
const skillDir = path17.join(".opencode", "skills", skill.name);
|
|
5833
|
-
if (!
|
|
5941
|
+
if (!fs18.existsSync(skillDir)) fs18.mkdirSync(skillDir, { recursive: true });
|
|
5834
5942
|
const skillPath = path17.join(skillDir, "SKILL.md");
|
|
5835
|
-
|
|
5943
|
+
fs18.writeFileSync(skillPath, buildSkillContent(skill));
|
|
5836
5944
|
written.push(skillPath);
|
|
5837
5945
|
}
|
|
5838
5946
|
}
|
|
@@ -5840,30 +5948,30 @@ function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
|
|
|
5840
5948
|
}
|
|
5841
5949
|
|
|
5842
5950
|
// src/writers/backup.ts
|
|
5843
|
-
import
|
|
5951
|
+
import fs19 from "fs";
|
|
5844
5952
|
import path18 from "path";
|
|
5845
5953
|
function createBackup(files) {
|
|
5846
5954
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5847
5955
|
const backupDir = path18.join(BACKUPS_DIR, timestamp);
|
|
5848
5956
|
for (const file of files) {
|
|
5849
|
-
if (!
|
|
5957
|
+
if (!fs19.existsSync(file)) continue;
|
|
5850
5958
|
const dest = path18.join(backupDir, file);
|
|
5851
5959
|
const destDir = path18.dirname(dest);
|
|
5852
|
-
if (!
|
|
5853
|
-
|
|
5960
|
+
if (!fs19.existsSync(destDir)) {
|
|
5961
|
+
fs19.mkdirSync(destDir, { recursive: true });
|
|
5854
5962
|
}
|
|
5855
|
-
|
|
5963
|
+
fs19.copyFileSync(file, dest);
|
|
5856
5964
|
}
|
|
5857
5965
|
return backupDir;
|
|
5858
5966
|
}
|
|
5859
5967
|
function restoreBackup(backupDir, file) {
|
|
5860
5968
|
const backupFile = path18.join(backupDir, file);
|
|
5861
|
-
if (!
|
|
5969
|
+
if (!fs19.existsSync(backupFile)) return false;
|
|
5862
5970
|
const destDir = path18.dirname(file);
|
|
5863
|
-
if (!
|
|
5864
|
-
|
|
5971
|
+
if (!fs19.existsSync(destDir)) {
|
|
5972
|
+
fs19.mkdirSync(destDir, { recursive: true });
|
|
5865
5973
|
}
|
|
5866
|
-
|
|
5974
|
+
fs19.copyFileSync(backupFile, file);
|
|
5867
5975
|
return true;
|
|
5868
5976
|
}
|
|
5869
5977
|
|
|
@@ -5871,32 +5979,32 @@ function restoreBackup(backupDir, file) {
|
|
|
5871
5979
|
init_builtin_skills();
|
|
5872
5980
|
|
|
5873
5981
|
// src/writers/manifest.ts
|
|
5874
|
-
import
|
|
5982
|
+
import fs20 from "fs";
|
|
5875
5983
|
import crypto3 from "crypto";
|
|
5876
5984
|
function readManifest() {
|
|
5877
5985
|
try {
|
|
5878
|
-
if (!
|
|
5879
|
-
return JSON.parse(
|
|
5986
|
+
if (!fs20.existsSync(MANIFEST_FILE)) return null;
|
|
5987
|
+
return JSON.parse(fs20.readFileSync(MANIFEST_FILE, "utf-8"));
|
|
5880
5988
|
} catch {
|
|
5881
5989
|
return null;
|
|
5882
5990
|
}
|
|
5883
5991
|
}
|
|
5884
5992
|
function writeManifest(manifest) {
|
|
5885
|
-
if (!
|
|
5886
|
-
|
|
5993
|
+
if (!fs20.existsSync(CALIBER_DIR)) {
|
|
5994
|
+
fs20.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
5887
5995
|
}
|
|
5888
|
-
|
|
5996
|
+
fs20.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
|
|
5889
5997
|
}
|
|
5890
5998
|
function fileChecksum(filePath) {
|
|
5891
|
-
const content =
|
|
5999
|
+
const content = fs20.readFileSync(filePath);
|
|
5892
6000
|
return crypto3.createHash("sha256").update(content).digest("hex");
|
|
5893
6001
|
}
|
|
5894
6002
|
|
|
5895
6003
|
// src/writers/index.ts
|
|
5896
6004
|
function writeSetup(setup) {
|
|
5897
6005
|
const filesToWrite = getFilesToWrite(setup);
|
|
5898
|
-
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) =>
|
|
5899
|
-
const existingFiles = [...filesToWrite.filter((f) =>
|
|
6006
|
+
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs21.existsSync(f));
|
|
6007
|
+
const existingFiles = [...filesToWrite.filter((f) => fs21.existsSync(f)), ...filesToDelete];
|
|
5900
6008
|
const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
|
|
5901
6009
|
const written = [];
|
|
5902
6010
|
if (setup.targetAgent.includes("claude") && setup.claude) {
|
|
@@ -5917,7 +6025,7 @@ function writeSetup(setup) {
|
|
|
5917
6025
|
}
|
|
5918
6026
|
const deleted = [];
|
|
5919
6027
|
for (const filePath of filesToDelete) {
|
|
5920
|
-
|
|
6028
|
+
fs21.unlinkSync(filePath);
|
|
5921
6029
|
deleted.push(filePath);
|
|
5922
6030
|
}
|
|
5923
6031
|
written.push(...ensureBuiltinSkills());
|
|
@@ -5948,8 +6056,8 @@ function undoSetup() {
|
|
|
5948
6056
|
const removed = [];
|
|
5949
6057
|
for (const entry of manifest.entries) {
|
|
5950
6058
|
if (entry.action === "created") {
|
|
5951
|
-
if (
|
|
5952
|
-
|
|
6059
|
+
if (fs21.existsSync(entry.path)) {
|
|
6060
|
+
fs21.unlinkSync(entry.path);
|
|
5953
6061
|
removed.push(entry.path);
|
|
5954
6062
|
}
|
|
5955
6063
|
} else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
|
|
@@ -5958,8 +6066,8 @@ function undoSetup() {
|
|
|
5958
6066
|
}
|
|
5959
6067
|
}
|
|
5960
6068
|
}
|
|
5961
|
-
if (
|
|
5962
|
-
|
|
6069
|
+
if (fs21.existsSync(MANIFEST_FILE)) {
|
|
6070
|
+
fs21.unlinkSync(MANIFEST_FILE);
|
|
5963
6071
|
}
|
|
5964
6072
|
return { restored, removed };
|
|
5965
6073
|
}
|
|
@@ -6012,18 +6120,18 @@ function getFilesToWrite(setup) {
|
|
|
6012
6120
|
}
|
|
6013
6121
|
function ensureGitignore() {
|
|
6014
6122
|
const gitignorePath = ".gitignore";
|
|
6015
|
-
if (
|
|
6016
|
-
const content =
|
|
6123
|
+
if (fs21.existsSync(gitignorePath)) {
|
|
6124
|
+
const content = fs21.readFileSync(gitignorePath, "utf-8");
|
|
6017
6125
|
if (!content.includes(".caliber/")) {
|
|
6018
|
-
|
|
6126
|
+
fs21.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
|
|
6019
6127
|
}
|
|
6020
6128
|
} else {
|
|
6021
|
-
|
|
6129
|
+
fs21.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
|
|
6022
6130
|
}
|
|
6023
6131
|
}
|
|
6024
6132
|
|
|
6025
6133
|
// src/writers/staging.ts
|
|
6026
|
-
import
|
|
6134
|
+
import fs22 from "fs";
|
|
6027
6135
|
import path19 from "path";
|
|
6028
6136
|
var STAGED_DIR = path19.join(CALIBER_DIR, "staged");
|
|
6029
6137
|
var PROPOSED_DIR = path19.join(STAGED_DIR, "proposed");
|
|
@@ -6039,19 +6147,19 @@ function stageFiles(files, projectDir) {
|
|
|
6039
6147
|
for (const file of files) {
|
|
6040
6148
|
assertPathWithinDir(file.path, projectDir);
|
|
6041
6149
|
const originalPath = path19.join(projectDir, file.path);
|
|
6042
|
-
if (
|
|
6043
|
-
const existing =
|
|
6150
|
+
if (fs22.existsSync(originalPath)) {
|
|
6151
|
+
const existing = fs22.readFileSync(originalPath, "utf-8");
|
|
6044
6152
|
if (normalizeContent(existing) === normalizeContent(file.content)) {
|
|
6045
6153
|
continue;
|
|
6046
6154
|
}
|
|
6047
6155
|
}
|
|
6048
6156
|
const proposedPath = path19.join(PROPOSED_DIR, file.path);
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
if (
|
|
6157
|
+
fs22.mkdirSync(path19.dirname(proposedPath), { recursive: true });
|
|
6158
|
+
fs22.writeFileSync(proposedPath, file.content);
|
|
6159
|
+
if (fs22.existsSync(originalPath)) {
|
|
6052
6160
|
const currentPath = path19.join(CURRENT_DIR, file.path);
|
|
6053
|
-
|
|
6054
|
-
|
|
6161
|
+
fs22.mkdirSync(path19.dirname(currentPath), { recursive: true });
|
|
6162
|
+
fs22.copyFileSync(originalPath, currentPath);
|
|
6055
6163
|
modifiedFiles++;
|
|
6056
6164
|
stagedFiles.push({
|
|
6057
6165
|
relativePath: file.path,
|
|
@@ -6068,14 +6176,14 @@ function stageFiles(files, projectDir) {
|
|
|
6068
6176
|
return { newFiles, modifiedFiles, stagedFiles };
|
|
6069
6177
|
}
|
|
6070
6178
|
function cleanupStaging() {
|
|
6071
|
-
if (
|
|
6072
|
-
|
|
6179
|
+
if (fs22.existsSync(STAGED_DIR)) {
|
|
6180
|
+
fs22.rmSync(STAGED_DIR, { recursive: true, force: true });
|
|
6073
6181
|
}
|
|
6074
6182
|
}
|
|
6075
6183
|
|
|
6076
6184
|
// src/commands/setup-files.ts
|
|
6077
6185
|
init_builtin_skills();
|
|
6078
|
-
import
|
|
6186
|
+
import fs23 from "fs";
|
|
6079
6187
|
function collectSetupFiles(setup, targetAgent) {
|
|
6080
6188
|
const files = [];
|
|
6081
6189
|
const claude = setup.claude;
|
|
@@ -6182,7 +6290,7 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
6182
6290
|
}
|
|
6183
6291
|
const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
|
|
6184
6292
|
const opencodeTargeted = targetAgent ? targetAgent.includes("opencode") : false;
|
|
6185
|
-
if ((codexTargeted || opencodeTargeted) && !
|
|
6293
|
+
if ((codexTargeted || opencodeTargeted) && !fs23.existsSync("AGENTS.md") && !(codex && codex.agentsMd) && !(opencode && opencode.agentsMd)) {
|
|
6186
6294
|
const agentRefs = [];
|
|
6187
6295
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
6188
6296
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
@@ -6201,7 +6309,7 @@ ${agentRefs.join(" ")}
|
|
|
6201
6309
|
|
|
6202
6310
|
// src/lib/learning-hooks.ts
|
|
6203
6311
|
init_resolve_caliber();
|
|
6204
|
-
import
|
|
6312
|
+
import fs24 from "fs";
|
|
6205
6313
|
import path20 from "path";
|
|
6206
6314
|
var SETTINGS_PATH2 = path20.join(".claude", "settings.json");
|
|
6207
6315
|
var HOOK_TAILS = [
|
|
@@ -6220,17 +6328,17 @@ function getHookConfigs() {
|
|
|
6220
6328
|
}));
|
|
6221
6329
|
}
|
|
6222
6330
|
function readSettings2() {
|
|
6223
|
-
if (!
|
|
6331
|
+
if (!fs24.existsSync(SETTINGS_PATH2)) return {};
|
|
6224
6332
|
try {
|
|
6225
|
-
return JSON.parse(
|
|
6333
|
+
return JSON.parse(fs24.readFileSync(SETTINGS_PATH2, "utf-8"));
|
|
6226
6334
|
} catch {
|
|
6227
6335
|
return {};
|
|
6228
6336
|
}
|
|
6229
6337
|
}
|
|
6230
6338
|
function writeSettings2(settings) {
|
|
6231
6339
|
const dir = path20.dirname(SETTINGS_PATH2);
|
|
6232
|
-
if (!
|
|
6233
|
-
|
|
6340
|
+
if (!fs24.existsSync(dir)) fs24.mkdirSync(dir, { recursive: true });
|
|
6341
|
+
fs24.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
|
|
6234
6342
|
}
|
|
6235
6343
|
function hasLearningHook(matchers, tail) {
|
|
6236
6344
|
return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
|
|
@@ -6272,17 +6380,17 @@ var CURSOR_HOOK_EVENTS = [
|
|
|
6272
6380
|
{ event: "sessionEnd", tail: "learn finalize --auto" }
|
|
6273
6381
|
];
|
|
6274
6382
|
function readCursorHooks() {
|
|
6275
|
-
if (!
|
|
6383
|
+
if (!fs24.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
|
|
6276
6384
|
try {
|
|
6277
|
-
return JSON.parse(
|
|
6385
|
+
return JSON.parse(fs24.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
|
|
6278
6386
|
} catch {
|
|
6279
6387
|
return { version: 1, hooks: {} };
|
|
6280
6388
|
}
|
|
6281
6389
|
}
|
|
6282
6390
|
function writeCursorHooks(config) {
|
|
6283
6391
|
const dir = path20.dirname(CURSOR_HOOKS_PATH);
|
|
6284
|
-
if (!
|
|
6285
|
-
|
|
6392
|
+
if (!fs24.existsSync(dir)) fs24.mkdirSync(dir, { recursive: true });
|
|
6393
|
+
fs24.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
|
|
6286
6394
|
}
|
|
6287
6395
|
function hasCursorHook(entries, tail) {
|
|
6288
6396
|
return entries.some((e) => isCaliberCommand(e.command, tail));
|
|
@@ -6354,7 +6462,7 @@ function removeLearningHooks() {
|
|
|
6354
6462
|
init_resolve_caliber();
|
|
6355
6463
|
|
|
6356
6464
|
// src/lib/state.ts
|
|
6357
|
-
import
|
|
6465
|
+
import fs25 from "fs";
|
|
6358
6466
|
import path21 from "path";
|
|
6359
6467
|
import { execSync as execSync9 } from "child_process";
|
|
6360
6468
|
var STATE_FILE = path21.join(CALIBER_DIR, ".caliber-state.json");
|
|
@@ -6368,8 +6476,8 @@ function normalizeTargetAgent(value) {
|
|
|
6368
6476
|
}
|
|
6369
6477
|
function readState() {
|
|
6370
6478
|
try {
|
|
6371
|
-
if (!
|
|
6372
|
-
const raw = JSON.parse(
|
|
6479
|
+
if (!fs25.existsSync(STATE_FILE)) return null;
|
|
6480
|
+
const raw = JSON.parse(fs25.readFileSync(STATE_FILE, "utf-8"));
|
|
6373
6481
|
if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
|
|
6374
6482
|
return raw;
|
|
6375
6483
|
} catch {
|
|
@@ -6377,10 +6485,10 @@ function readState() {
|
|
|
6377
6485
|
}
|
|
6378
6486
|
}
|
|
6379
6487
|
function writeState(state) {
|
|
6380
|
-
if (!
|
|
6381
|
-
|
|
6488
|
+
if (!fs25.existsSync(CALIBER_DIR)) {
|
|
6489
|
+
fs25.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
6382
6490
|
}
|
|
6383
|
-
|
|
6491
|
+
fs25.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
6384
6492
|
}
|
|
6385
6493
|
function getCurrentHeadSha() {
|
|
6386
6494
|
try {
|
|
@@ -6397,6 +6505,7 @@ function getCurrentHeadSha() {
|
|
|
6397
6505
|
import chalk2 from "chalk";
|
|
6398
6506
|
import readline from "readline";
|
|
6399
6507
|
function promptInput(question) {
|
|
6508
|
+
if (!process.stdin.isTTY) return Promise.resolve("");
|
|
6400
6509
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
6401
6510
|
return new Promise((resolve3) => {
|
|
6402
6511
|
rl.question(chalk2.cyan(`${question} `), (answer) => {
|
|
@@ -7487,7 +7596,7 @@ function checkSources(dir) {
|
|
|
7487
7596
|
}
|
|
7488
7597
|
|
|
7489
7598
|
// src/scoring/dismissed.ts
|
|
7490
|
-
import
|
|
7599
|
+
import fs26 from "fs";
|
|
7491
7600
|
import path22 from "path";
|
|
7492
7601
|
var DISMISSED_FILE = path22.join(CALIBER_DIR, "dismissed-checks.json");
|
|
7493
7602
|
function dismissedFilePath(dir) {
|
|
@@ -7496,17 +7605,17 @@ function dismissedFilePath(dir) {
|
|
|
7496
7605
|
function readDismissedChecks(dir) {
|
|
7497
7606
|
try {
|
|
7498
7607
|
const filePath = dismissedFilePath(dir);
|
|
7499
|
-
if (!
|
|
7500
|
-
return JSON.parse(
|
|
7608
|
+
if (!fs26.existsSync(filePath)) return [];
|
|
7609
|
+
return JSON.parse(fs26.readFileSync(filePath, "utf-8"));
|
|
7501
7610
|
} catch {
|
|
7502
7611
|
return [];
|
|
7503
7612
|
}
|
|
7504
7613
|
}
|
|
7505
7614
|
function writeDismissedChecks(checks) {
|
|
7506
|
-
if (!
|
|
7507
|
-
|
|
7615
|
+
if (!fs26.existsSync(CALIBER_DIR)) {
|
|
7616
|
+
fs26.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
7508
7617
|
}
|
|
7509
|
-
|
|
7618
|
+
fs26.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
|
|
7510
7619
|
}
|
|
7511
7620
|
function getDismissedIds(dir) {
|
|
7512
7621
|
return new Set(readDismissedChecks(dir).map((c) => c.id));
|
|
@@ -7765,7 +7874,7 @@ import { PostHog } from "posthog-node";
|
|
|
7765
7874
|
import chalk5 from "chalk";
|
|
7766
7875
|
|
|
7767
7876
|
// src/telemetry/config.ts
|
|
7768
|
-
import
|
|
7877
|
+
import fs27 from "fs";
|
|
7769
7878
|
import path23 from "path";
|
|
7770
7879
|
import os5 from "os";
|
|
7771
7880
|
import crypto4 from "crypto";
|
|
@@ -7775,17 +7884,17 @@ var CONFIG_FILE2 = path23.join(CONFIG_DIR2, "config.json");
|
|
|
7775
7884
|
var runtimeDisabled = false;
|
|
7776
7885
|
function readConfig() {
|
|
7777
7886
|
try {
|
|
7778
|
-
if (!
|
|
7779
|
-
return JSON.parse(
|
|
7887
|
+
if (!fs27.existsSync(CONFIG_FILE2)) return {};
|
|
7888
|
+
return JSON.parse(fs27.readFileSync(CONFIG_FILE2, "utf-8"));
|
|
7780
7889
|
} catch {
|
|
7781
7890
|
return {};
|
|
7782
7891
|
}
|
|
7783
7892
|
}
|
|
7784
7893
|
function writeConfig(config) {
|
|
7785
|
-
if (!
|
|
7786
|
-
|
|
7894
|
+
if (!fs27.existsSync(CONFIG_DIR2)) {
|
|
7895
|
+
fs27.mkdirSync(CONFIG_DIR2, { recursive: true });
|
|
7787
7896
|
}
|
|
7788
|
-
|
|
7897
|
+
fs27.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
7789
7898
|
}
|
|
7790
7899
|
function getMachineId() {
|
|
7791
7900
|
const config = readConfig();
|
|
@@ -8458,6 +8567,10 @@ async function recommendCommand(options) {
|
|
|
8458
8567
|
await querySkills(options.query);
|
|
8459
8568
|
return;
|
|
8460
8569
|
}
|
|
8570
|
+
if (!process.stdin.isTTY) {
|
|
8571
|
+
console.log(chalk6.dim(" Skills search requires an interactive terminal."));
|
|
8572
|
+
return;
|
|
8573
|
+
}
|
|
8461
8574
|
const proceed = await select3({
|
|
8462
8575
|
message: "Search public repos for relevant skills to add to this project?",
|
|
8463
8576
|
choices: [
|
|
@@ -9125,7 +9238,7 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory, options) {
|
|
|
9125
9238
|
}
|
|
9126
9239
|
|
|
9127
9240
|
// src/lib/debug-report.ts
|
|
9128
|
-
import
|
|
9241
|
+
import fs28 from "fs";
|
|
9129
9242
|
import path24 from "path";
|
|
9130
9243
|
var DebugReport = class {
|
|
9131
9244
|
sections = [];
|
|
@@ -9196,10 +9309,10 @@ var DebugReport = class {
|
|
|
9196
9309
|
lines.push("");
|
|
9197
9310
|
}
|
|
9198
9311
|
const dir = path24.dirname(outputPath);
|
|
9199
|
-
if (!
|
|
9200
|
-
|
|
9312
|
+
if (!fs28.existsSync(dir)) {
|
|
9313
|
+
fs28.mkdirSync(dir, { recursive: true });
|
|
9201
9314
|
}
|
|
9202
|
-
|
|
9315
|
+
fs28.writeFileSync(outputPath, lines.join("\n"));
|
|
9203
9316
|
}
|
|
9204
9317
|
};
|
|
9205
9318
|
function formatMs(ms) {
|
|
@@ -9600,7 +9713,7 @@ import chalk11 from "chalk";
|
|
|
9600
9713
|
import ora3 from "ora";
|
|
9601
9714
|
import select5 from "@inquirer/select";
|
|
9602
9715
|
import checkbox from "@inquirer/checkbox";
|
|
9603
|
-
import
|
|
9716
|
+
import fs31 from "fs";
|
|
9604
9717
|
|
|
9605
9718
|
// src/ai/refine.ts
|
|
9606
9719
|
async function refineSetup(currentSetup, message, conversationHistory, callbacks) {
|
|
@@ -9776,11 +9889,11 @@ init_config();
|
|
|
9776
9889
|
init_review();
|
|
9777
9890
|
function detectAgents(dir) {
|
|
9778
9891
|
const agents = [];
|
|
9779
|
-
if (
|
|
9780
|
-
if (
|
|
9781
|
-
if (
|
|
9782
|
-
if (
|
|
9783
|
-
if (
|
|
9892
|
+
if (fs31.existsSync(`${dir}/.claude`)) agents.push("claude");
|
|
9893
|
+
if (fs31.existsSync(`${dir}/.cursor`)) agents.push("cursor");
|
|
9894
|
+
if (fs31.existsSync(`${dir}/.agents`) || fs31.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
|
|
9895
|
+
if (fs31.existsSync(`${dir}/.opencode`)) agents.push("opencode");
|
|
9896
|
+
if (fs31.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
|
|
9784
9897
|
return agents;
|
|
9785
9898
|
}
|
|
9786
9899
|
async function promptAgent(detected) {
|
|
@@ -9884,7 +9997,7 @@ async function refineLoop(currentSetup, sessionHistory, summarizeSetup2, printSu
|
|
|
9884
9997
|
|
|
9885
9998
|
// src/commands/init-display.ts
|
|
9886
9999
|
import chalk12 from "chalk";
|
|
9887
|
-
import
|
|
10000
|
+
import fs32 from "fs";
|
|
9888
10001
|
init_types();
|
|
9889
10002
|
function formatWhatChanged(setup) {
|
|
9890
10003
|
const lines = [];
|
|
@@ -9892,12 +10005,12 @@ function formatWhatChanged(setup) {
|
|
|
9892
10005
|
const codex = setup.codex;
|
|
9893
10006
|
const cursor = setup.cursor;
|
|
9894
10007
|
if (claude?.claudeMd) {
|
|
9895
|
-
const action =
|
|
10008
|
+
const action = fs32.existsSync("CLAUDE.md") ? "Updated" : "Created";
|
|
9896
10009
|
lines.push(`${action} CLAUDE.md`);
|
|
9897
10010
|
}
|
|
9898
10011
|
const opencode = setup.opencode;
|
|
9899
10012
|
if (codex?.agentsMd || opencode?.agentsMd) {
|
|
9900
|
-
const action =
|
|
10013
|
+
const action = fs32.existsSync("AGENTS.md") ? "Updated" : "Created";
|
|
9901
10014
|
lines.push(`${action} AGENTS.md`);
|
|
9902
10015
|
}
|
|
9903
10016
|
const allSkills = [];
|
|
@@ -9941,7 +10054,7 @@ function printSetupSummary(setup) {
|
|
|
9941
10054
|
};
|
|
9942
10055
|
if (claude) {
|
|
9943
10056
|
if (claude.claudeMd) {
|
|
9944
|
-
const icon =
|
|
10057
|
+
const icon = fs32.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
|
|
9945
10058
|
const desc = getDescription("CLAUDE.md");
|
|
9946
10059
|
console.log(` ${icon} ${chalk12.bold("CLAUDE.md")}`);
|
|
9947
10060
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -9951,7 +10064,7 @@ function printSetupSummary(setup) {
|
|
|
9951
10064
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
9952
10065
|
for (const skill of skills) {
|
|
9953
10066
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
9954
|
-
const icon =
|
|
10067
|
+
const icon = fs32.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
9955
10068
|
const desc = getDescription(skillPath);
|
|
9956
10069
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
9957
10070
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -9962,7 +10075,7 @@ function printSetupSummary(setup) {
|
|
|
9962
10075
|
const codex = setup.codex;
|
|
9963
10076
|
if (codex) {
|
|
9964
10077
|
if (codex.agentsMd) {
|
|
9965
|
-
const icon =
|
|
10078
|
+
const icon = fs32.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
|
|
9966
10079
|
const desc = getDescription("AGENTS.md");
|
|
9967
10080
|
console.log(` ${icon} ${chalk12.bold("AGENTS.md")}`);
|
|
9968
10081
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -9972,7 +10085,7 @@ function printSetupSummary(setup) {
|
|
|
9972
10085
|
if (Array.isArray(codexSkills) && codexSkills.length > 0) {
|
|
9973
10086
|
for (const skill of codexSkills) {
|
|
9974
10087
|
const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
|
|
9975
|
-
const icon =
|
|
10088
|
+
const icon = fs32.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
9976
10089
|
const desc = getDescription(skillPath);
|
|
9977
10090
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
9978
10091
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -9983,7 +10096,7 @@ function printSetupSummary(setup) {
|
|
|
9983
10096
|
const opencode = setup.opencode;
|
|
9984
10097
|
if (opencode) {
|
|
9985
10098
|
if (opencode.agentsMd && !codex?.agentsMd) {
|
|
9986
|
-
const icon =
|
|
10099
|
+
const icon = fs32.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
|
|
9987
10100
|
const desc = getDescription("AGENTS.md");
|
|
9988
10101
|
console.log(` ${icon} ${chalk12.bold("AGENTS.md")} ${chalk12.dim("(OpenCode)")}`);
|
|
9989
10102
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -9993,7 +10106,7 @@ function printSetupSummary(setup) {
|
|
|
9993
10106
|
if (Array.isArray(opencodeSkills) && opencodeSkills.length > 0) {
|
|
9994
10107
|
for (const skill of opencodeSkills) {
|
|
9995
10108
|
const skillPath = `.opencode/skills/${skill.name}/SKILL.md`;
|
|
9996
|
-
const icon =
|
|
10109
|
+
const icon = fs32.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
9997
10110
|
const desc = getDescription(skillPath);
|
|
9998
10111
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
9999
10112
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -10003,7 +10116,7 @@ function printSetupSummary(setup) {
|
|
|
10003
10116
|
}
|
|
10004
10117
|
if (cursor) {
|
|
10005
10118
|
if (cursor.cursorrules) {
|
|
10006
|
-
const icon =
|
|
10119
|
+
const icon = fs32.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
|
|
10007
10120
|
const desc = getDescription(".cursorrules");
|
|
10008
10121
|
console.log(` ${icon} ${chalk12.bold(".cursorrules")}`);
|
|
10009
10122
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -10013,7 +10126,7 @@ function printSetupSummary(setup) {
|
|
|
10013
10126
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
10014
10127
|
for (const skill of cursorSkills) {
|
|
10015
10128
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
10016
|
-
const icon =
|
|
10129
|
+
const icon = fs32.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
10017
10130
|
const desc = getDescription(skillPath);
|
|
10018
10131
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
10019
10132
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -10024,7 +10137,7 @@ function printSetupSummary(setup) {
|
|
|
10024
10137
|
if (Array.isArray(rulesArr) && rulesArr.length > 0) {
|
|
10025
10138
|
for (const rule of rulesArr) {
|
|
10026
10139
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
10027
|
-
const icon =
|
|
10140
|
+
const icon = fs32.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
10028
10141
|
const desc = getDescription(rulePath);
|
|
10029
10142
|
console.log(` ${icon} ${chalk12.bold(rulePath)}`);
|
|
10030
10143
|
if (desc) {
|
|
@@ -10086,12 +10199,12 @@ function displayTokenUsage() {
|
|
|
10086
10199
|
// src/commands/init-helpers.ts
|
|
10087
10200
|
init_config();
|
|
10088
10201
|
import chalk13 from "chalk";
|
|
10089
|
-
import
|
|
10202
|
+
import fs33 from "fs";
|
|
10090
10203
|
import path26 from "path";
|
|
10091
10204
|
function isFirstRun(dir) {
|
|
10092
10205
|
const caliberDir = path26.join(dir, ".caliber");
|
|
10093
10206
|
try {
|
|
10094
|
-
const stat =
|
|
10207
|
+
const stat = fs33.statSync(caliberDir);
|
|
10095
10208
|
return !stat.isDirectory();
|
|
10096
10209
|
} catch {
|
|
10097
10210
|
return true;
|
|
@@ -10144,8 +10257,8 @@ function ensurePermissions(fingerprint) {
|
|
|
10144
10257
|
const settingsPath = ".claude/settings.json";
|
|
10145
10258
|
let settings = {};
|
|
10146
10259
|
try {
|
|
10147
|
-
if (
|
|
10148
|
-
settings = JSON.parse(
|
|
10260
|
+
if (fs33.existsSync(settingsPath)) {
|
|
10261
|
+
settings = JSON.parse(fs33.readFileSync(settingsPath, "utf-8"));
|
|
10149
10262
|
}
|
|
10150
10263
|
} catch {
|
|
10151
10264
|
}
|
|
@@ -10154,8 +10267,8 @@ function ensurePermissions(fingerprint) {
|
|
|
10154
10267
|
if (Array.isArray(allow) && allow.length > 0) return;
|
|
10155
10268
|
permissions.allow = derivePermissions(fingerprint);
|
|
10156
10269
|
settings.permissions = permissions;
|
|
10157
|
-
if (!
|
|
10158
|
-
|
|
10270
|
+
if (!fs33.existsSync(".claude")) fs33.mkdirSync(".claude", { recursive: true });
|
|
10271
|
+
fs33.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
10159
10272
|
}
|
|
10160
10273
|
function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
10161
10274
|
try {
|
|
@@ -10184,8 +10297,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
|
10184
10297
|
lines.push("```");
|
|
10185
10298
|
lines.push("", "If timeouts persist, try a different model.");
|
|
10186
10299
|
}
|
|
10187
|
-
|
|
10188
|
-
|
|
10300
|
+
fs33.mkdirSync(path26.join(process.cwd(), ".caliber"), { recursive: true });
|
|
10301
|
+
fs33.writeFileSync(logPath, lines.join("\n"));
|
|
10189
10302
|
console.log(chalk13.dim(`
|
|
10190
10303
|
Error log written to .caliber/error-log.md`));
|
|
10191
10304
|
} catch {
|
|
@@ -10238,7 +10351,7 @@ ${JSON.stringify(checkList, null, 2)}`,
|
|
|
10238
10351
|
}
|
|
10239
10352
|
|
|
10240
10353
|
// src/scoring/history.ts
|
|
10241
|
-
import
|
|
10354
|
+
import fs34 from "fs";
|
|
10242
10355
|
import path27 from "path";
|
|
10243
10356
|
var HISTORY_FILE = "score-history.jsonl";
|
|
10244
10357
|
var MAX_ENTRIES = 500;
|
|
@@ -10255,14 +10368,14 @@ function recordScore(result, trigger) {
|
|
|
10255
10368
|
trigger
|
|
10256
10369
|
};
|
|
10257
10370
|
try {
|
|
10258
|
-
|
|
10371
|
+
fs34.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
10259
10372
|
const filePath = historyFilePath();
|
|
10260
|
-
|
|
10261
|
-
const stat =
|
|
10373
|
+
fs34.appendFileSync(filePath, JSON.stringify(entry) + "\n");
|
|
10374
|
+
const stat = fs34.statSync(filePath);
|
|
10262
10375
|
if (stat.size > TRIM_THRESHOLD * 120) {
|
|
10263
|
-
const lines =
|
|
10376
|
+
const lines = fs34.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
10264
10377
|
if (lines.length > MAX_ENTRIES) {
|
|
10265
|
-
|
|
10378
|
+
fs34.writeFileSync(filePath, lines.slice(-MAX_ENTRIES).join("\n") + "\n");
|
|
10266
10379
|
}
|
|
10267
10380
|
}
|
|
10268
10381
|
} catch {
|
|
@@ -10271,7 +10384,7 @@ function recordScore(result, trigger) {
|
|
|
10271
10384
|
function readScoreHistory() {
|
|
10272
10385
|
const filePath = historyFilePath();
|
|
10273
10386
|
try {
|
|
10274
|
-
const content =
|
|
10387
|
+
const content = fs34.readFileSync(filePath, "utf-8");
|
|
10275
10388
|
const entries = [];
|
|
10276
10389
|
for (const line of content.split("\n")) {
|
|
10277
10390
|
if (!line) continue;
|
|
@@ -10450,12 +10563,12 @@ async function initCommand(options) {
|
|
|
10450
10563
|
}
|
|
10451
10564
|
const { ensureBuiltinSkills: ensureBuiltinSkills2 } = await Promise.resolve().then(() => (init_builtin_skills(), builtin_skills_exports));
|
|
10452
10565
|
for (const agent of targetAgent) {
|
|
10453
|
-
if (agent === "claude" && !
|
|
10454
|
-
|
|
10455
|
-
if (agent === "cursor" && !
|
|
10456
|
-
|
|
10457
|
-
if (agent === "codex" && !
|
|
10458
|
-
|
|
10566
|
+
if (agent === "claude" && !fs35.existsSync(".claude"))
|
|
10567
|
+
fs35.mkdirSync(".claude", { recursive: true });
|
|
10568
|
+
if (agent === "cursor" && !fs35.existsSync(".cursor"))
|
|
10569
|
+
fs35.mkdirSync(".cursor", { recursive: true });
|
|
10570
|
+
if (agent === "codex" && !fs35.existsSync(".agents"))
|
|
10571
|
+
fs35.mkdirSync(".agents", { recursive: true });
|
|
10459
10572
|
}
|
|
10460
10573
|
const skillsWritten = ensureBuiltinSkills2();
|
|
10461
10574
|
if (skillsWritten.length > 0) {
|
|
@@ -10544,7 +10657,7 @@ async function initCommand(options) {
|
|
|
10544
10657
|
const claudeMdPath = "CLAUDE.md";
|
|
10545
10658
|
let claudeContent = "";
|
|
10546
10659
|
try {
|
|
10547
|
-
claudeContent =
|
|
10660
|
+
claudeContent = fs35.readFileSync(claudeMdPath, "utf-8");
|
|
10548
10661
|
} catch {
|
|
10549
10662
|
}
|
|
10550
10663
|
if (!claudeContent) {
|
|
@@ -10552,20 +10665,20 @@ async function initCommand(options) {
|
|
|
10552
10665
|
`;
|
|
10553
10666
|
}
|
|
10554
10667
|
const updatedClaude = appendManagedBlocks2(claudeContent, "claude");
|
|
10555
|
-
if (updatedClaude !== claudeContent || !
|
|
10556
|
-
|
|
10668
|
+
if (updatedClaude !== claudeContent || !fs35.existsSync(claudeMdPath)) {
|
|
10669
|
+
fs35.writeFileSync(claudeMdPath, updatedClaude);
|
|
10557
10670
|
console.log(` ${chalk14.green("\u2713")} CLAUDE.md \u2014 added Caliber sync instructions`);
|
|
10558
10671
|
}
|
|
10559
10672
|
if (targetAgent.includes("cursor")) {
|
|
10560
10673
|
const rulesDir = path28.join(".cursor", "rules");
|
|
10561
|
-
if (!
|
|
10674
|
+
if (!fs35.existsSync(rulesDir)) fs35.mkdirSync(rulesDir, { recursive: true });
|
|
10562
10675
|
for (const rule of [
|
|
10563
10676
|
getCursorPreCommitRule2(),
|
|
10564
10677
|
getCursorLearningsRule2(),
|
|
10565
10678
|
getCursorSyncRule2(),
|
|
10566
10679
|
getCursorSetupRule2()
|
|
10567
10680
|
]) {
|
|
10568
|
-
|
|
10681
|
+
fs35.writeFileSync(path28.join(rulesDir, rule.filename), rule.content);
|
|
10569
10682
|
}
|
|
10570
10683
|
console.log(` ${chalk14.green("\u2713")} Cursor rules \u2014 added Caliber sync rules`);
|
|
10571
10684
|
}
|
|
@@ -10573,17 +10686,17 @@ async function initCommand(options) {
|
|
|
10573
10686
|
const copilotPath = path28.join(".github", "copilot-instructions.md");
|
|
10574
10687
|
let copilotContent = "";
|
|
10575
10688
|
try {
|
|
10576
|
-
copilotContent =
|
|
10689
|
+
copilotContent = fs35.readFileSync(copilotPath, "utf-8");
|
|
10577
10690
|
} catch {
|
|
10578
10691
|
}
|
|
10579
10692
|
if (!copilotContent) {
|
|
10580
|
-
|
|
10693
|
+
fs35.mkdirSync(".github", { recursive: true });
|
|
10581
10694
|
copilotContent = `# ${path28.basename(process.cwd())}
|
|
10582
10695
|
`;
|
|
10583
10696
|
}
|
|
10584
10697
|
const updatedCopilot = appendManagedBlocks2(copilotContent, "copilot");
|
|
10585
10698
|
if (updatedCopilot !== copilotContent) {
|
|
10586
|
-
|
|
10699
|
+
fs35.writeFileSync(copilotPath, updatedCopilot);
|
|
10587
10700
|
console.log(` ${chalk14.green("\u2713")} Copilot instructions \u2014 added Caliber sync instructions`);
|
|
10588
10701
|
}
|
|
10589
10702
|
}
|
|
@@ -10930,7 +11043,7 @@ async function initCommand(options) {
|
|
|
10930
11043
|
const { default: ora9 } = await import("ora");
|
|
10931
11044
|
const writeSpinner = ora9("Writing config files...").start();
|
|
10932
11045
|
try {
|
|
10933
|
-
if (targetAgent.includes("codex") && !
|
|
11046
|
+
if (targetAgent.includes("codex") && !fs35.existsSync("AGENTS.md") && !generatedSetup.codex) {
|
|
10934
11047
|
const claude = generatedSetup.claude;
|
|
10935
11048
|
const cursor = generatedSetup.cursor;
|
|
10936
11049
|
const agentRefs = [];
|
|
@@ -11120,7 +11233,7 @@ function undoCommand() {
|
|
|
11120
11233
|
|
|
11121
11234
|
// src/commands/status.ts
|
|
11122
11235
|
import chalk16 from "chalk";
|
|
11123
|
-
import
|
|
11236
|
+
import fs36 from "fs";
|
|
11124
11237
|
init_config();
|
|
11125
11238
|
init_resolve_caliber();
|
|
11126
11239
|
async function statusCommand(options) {
|
|
@@ -11149,7 +11262,7 @@ async function statusCommand(options) {
|
|
|
11149
11262
|
}
|
|
11150
11263
|
console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
|
|
11151
11264
|
for (const entry of manifest.entries) {
|
|
11152
|
-
const exists =
|
|
11265
|
+
const exists = fs36.existsSync(entry.path);
|
|
11153
11266
|
const icon = exists ? chalk16.green("\u2713") : chalk16.red("\u2717");
|
|
11154
11267
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
11155
11268
|
}
|
|
@@ -11306,42 +11419,42 @@ async function regenerateCommand(options) {
|
|
|
11306
11419
|
}
|
|
11307
11420
|
|
|
11308
11421
|
// src/commands/score.ts
|
|
11309
|
-
import
|
|
11422
|
+
import fs37 from "fs";
|
|
11310
11423
|
import os7 from "os";
|
|
11311
11424
|
import path29 from "path";
|
|
11312
|
-
import { execFileSync as
|
|
11425
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
11313
11426
|
import chalk18 from "chalk";
|
|
11314
11427
|
init_resolve_caliber();
|
|
11315
11428
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS.md"];
|
|
11316
11429
|
var CONFIG_DIRS = [".claude", ".cursor"];
|
|
11317
11430
|
function scoreBaseRef(ref, target) {
|
|
11318
11431
|
if (!/^[\w.\-/~^@{}]+$/.test(ref)) return null;
|
|
11319
|
-
const tmpDir =
|
|
11432
|
+
const tmpDir = fs37.mkdtempSync(path29.join(os7.tmpdir(), "caliber-compare-"));
|
|
11320
11433
|
try {
|
|
11321
11434
|
for (const file of CONFIG_FILES) {
|
|
11322
11435
|
try {
|
|
11323
|
-
const content =
|
|
11436
|
+
const content = execFileSync4("git", ["show", `${ref}:${file}`], {
|
|
11324
11437
|
encoding: "utf-8",
|
|
11325
11438
|
stdio: ["pipe", "pipe", "pipe"]
|
|
11326
11439
|
});
|
|
11327
|
-
|
|
11440
|
+
fs37.writeFileSync(path29.join(tmpDir, file), content);
|
|
11328
11441
|
} catch {
|
|
11329
11442
|
}
|
|
11330
11443
|
}
|
|
11331
11444
|
for (const dir of CONFIG_DIRS) {
|
|
11332
11445
|
try {
|
|
11333
|
-
const files =
|
|
11446
|
+
const files = execFileSync4("git", ["ls-tree", "-r", "--name-only", ref, `${dir}/`], {
|
|
11334
11447
|
encoding: "utf-8",
|
|
11335
11448
|
stdio: ["pipe", "pipe", "pipe"]
|
|
11336
11449
|
}).trim().split("\n").filter(Boolean);
|
|
11337
11450
|
for (const file of files) {
|
|
11338
11451
|
const filePath = path29.join(tmpDir, file);
|
|
11339
|
-
|
|
11340
|
-
const content =
|
|
11452
|
+
fs37.mkdirSync(path29.dirname(filePath), { recursive: true });
|
|
11453
|
+
const content = execFileSync4("git", ["show", `${ref}:${file}`], {
|
|
11341
11454
|
encoding: "utf-8",
|
|
11342
11455
|
stdio: ["pipe", "pipe", "pipe"]
|
|
11343
11456
|
});
|
|
11344
|
-
|
|
11457
|
+
fs37.writeFileSync(filePath, content);
|
|
11345
11458
|
}
|
|
11346
11459
|
} catch {
|
|
11347
11460
|
}
|
|
@@ -11351,7 +11464,7 @@ function scoreBaseRef(ref, target) {
|
|
|
11351
11464
|
} catch {
|
|
11352
11465
|
return null;
|
|
11353
11466
|
} finally {
|
|
11354
|
-
|
|
11467
|
+
fs37.rmSync(tmpDir, { recursive: true, force: true });
|
|
11355
11468
|
}
|
|
11356
11469
|
}
|
|
11357
11470
|
async function scoreCommand(options) {
|
|
@@ -11444,10 +11557,11 @@ async function scoreCommand(options) {
|
|
|
11444
11557
|
}
|
|
11445
11558
|
|
|
11446
11559
|
// src/commands/refresh.ts
|
|
11447
|
-
import
|
|
11560
|
+
import fs42 from "fs";
|
|
11448
11561
|
import path34 from "path";
|
|
11449
11562
|
import chalk19 from "chalk";
|
|
11450
11563
|
import ora6 from "ora";
|
|
11564
|
+
import pLimit from "p-limit";
|
|
11451
11565
|
|
|
11452
11566
|
// src/lib/git-diff.ts
|
|
11453
11567
|
import { execSync as execSync15 } from "child_process";
|
|
@@ -11560,13 +11674,13 @@ function scopeDiffToDir(diff, dir, allConfigDirs) {
|
|
|
11560
11674
|
|
|
11561
11675
|
// src/writers/refresh.ts
|
|
11562
11676
|
init_pre_commit_block();
|
|
11563
|
-
import
|
|
11677
|
+
import fs38 from "fs";
|
|
11564
11678
|
import path30 from "path";
|
|
11565
11679
|
function writeFileGroup(groupDir, files) {
|
|
11566
|
-
|
|
11680
|
+
fs38.mkdirSync(groupDir, { recursive: true });
|
|
11567
11681
|
return files.map((file) => {
|
|
11568
11682
|
const filePath = path30.join(groupDir, file.filename);
|
|
11569
|
-
|
|
11683
|
+
fs38.writeFileSync(filePath, file.content);
|
|
11570
11684
|
return filePath.replace(/\\/g, "/");
|
|
11571
11685
|
});
|
|
11572
11686
|
}
|
|
@@ -11575,18 +11689,18 @@ function writeRefreshDocs(docs, dir = ".") {
|
|
|
11575
11689
|
const p = (relPath) => (dir === "." ? relPath : path30.join(dir, relPath)).replace(/\\/g, "/");
|
|
11576
11690
|
const ensureParent = (filePath) => {
|
|
11577
11691
|
const parent = path30.dirname(filePath);
|
|
11578
|
-
if (parent !== "." && !
|
|
11692
|
+
if (parent !== "." && !fs38.existsSync(parent)) fs38.mkdirSync(parent, { recursive: true });
|
|
11579
11693
|
};
|
|
11580
11694
|
if (docs.agentsMd) {
|
|
11581
11695
|
const filePath = p("AGENTS.md");
|
|
11582
11696
|
ensureParent(filePath);
|
|
11583
|
-
|
|
11697
|
+
fs38.writeFileSync(filePath, appendManagedBlocks(docs.agentsMd, "codex"));
|
|
11584
11698
|
written.push(filePath);
|
|
11585
11699
|
}
|
|
11586
11700
|
if (docs.claudeMd) {
|
|
11587
11701
|
const filePath = p("CLAUDE.md");
|
|
11588
11702
|
ensureParent(filePath);
|
|
11589
|
-
|
|
11703
|
+
fs38.writeFileSync(filePath, appendManagedBlocks(docs.claudeMd));
|
|
11590
11704
|
written.push(filePath);
|
|
11591
11705
|
}
|
|
11592
11706
|
if (docs.claudeRules) {
|
|
@@ -11595,13 +11709,13 @@ function writeRefreshDocs(docs, dir = ".") {
|
|
|
11595
11709
|
if (docs.readmeMd) {
|
|
11596
11710
|
const filePath = p("README.md");
|
|
11597
11711
|
ensureParent(filePath);
|
|
11598
|
-
|
|
11712
|
+
fs38.writeFileSync(filePath, docs.readmeMd);
|
|
11599
11713
|
written.push(filePath);
|
|
11600
11714
|
}
|
|
11601
11715
|
if (docs.cursorrules) {
|
|
11602
11716
|
const filePath = p(".cursorrules");
|
|
11603
11717
|
ensureParent(filePath);
|
|
11604
|
-
|
|
11718
|
+
fs38.writeFileSync(filePath, docs.cursorrules);
|
|
11605
11719
|
written.push(filePath);
|
|
11606
11720
|
}
|
|
11607
11721
|
if (docs.cursorRules) {
|
|
@@ -11610,7 +11724,7 @@ function writeRefreshDocs(docs, dir = ".") {
|
|
|
11610
11724
|
if (docs.copilotInstructions) {
|
|
11611
11725
|
const filePath = p(path30.join(".github", "copilot-instructions.md"));
|
|
11612
11726
|
ensureParent(filePath);
|
|
11613
|
-
|
|
11727
|
+
fs38.writeFileSync(filePath, appendManagedBlocks(docs.copilotInstructions, "copilot"));
|
|
11614
11728
|
written.push(filePath);
|
|
11615
11729
|
}
|
|
11616
11730
|
if (docs.copilotInstructionFiles) {
|
|
@@ -11752,7 +11866,7 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
11752
11866
|
}
|
|
11753
11867
|
|
|
11754
11868
|
// src/learner/writer.ts
|
|
11755
|
-
import
|
|
11869
|
+
import fs39 from "fs";
|
|
11756
11870
|
import path31 from "path";
|
|
11757
11871
|
|
|
11758
11872
|
// src/learner/utils.ts
|
|
@@ -11870,8 +11984,8 @@ function deduplicateLearnedItems(existing, incoming) {
|
|
|
11870
11984
|
}
|
|
11871
11985
|
function writeLearnedSectionTo(filePath, header, existing, incoming, mode) {
|
|
11872
11986
|
const { merged, newCount, newItems } = deduplicateLearnedItems(existing, incoming);
|
|
11873
|
-
|
|
11874
|
-
if (mode)
|
|
11987
|
+
fs39.writeFileSync(filePath, header + merged + "\n");
|
|
11988
|
+
if (mode) fs39.chmodSync(filePath, mode);
|
|
11875
11989
|
return { newCount, newItems };
|
|
11876
11990
|
}
|
|
11877
11991
|
function writeLearnedSection(content) {
|
|
@@ -11879,11 +11993,11 @@ function writeLearnedSection(content) {
|
|
|
11879
11993
|
}
|
|
11880
11994
|
function writeLearnedSkill(skill) {
|
|
11881
11995
|
const skillDir = path31.join(".claude", "skills", skill.name);
|
|
11882
|
-
if (!
|
|
11996
|
+
if (!fs39.existsSync(skillDir)) fs39.mkdirSync(skillDir, { recursive: true });
|
|
11883
11997
|
const skillPath = path31.join(skillDir, "SKILL.md");
|
|
11884
|
-
if (!skill.isNew &&
|
|
11885
|
-
const existing =
|
|
11886
|
-
|
|
11998
|
+
if (!skill.isNew && fs39.existsSync(skillPath)) {
|
|
11999
|
+
const existing = fs39.readFileSync(skillPath, "utf-8");
|
|
12000
|
+
fs39.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
11887
12001
|
} else {
|
|
11888
12002
|
const frontmatter = [
|
|
11889
12003
|
"---",
|
|
@@ -11892,12 +12006,12 @@ function writeLearnedSkill(skill) {
|
|
|
11892
12006
|
"---",
|
|
11893
12007
|
""
|
|
11894
12008
|
].join("\n");
|
|
11895
|
-
|
|
12009
|
+
fs39.writeFileSync(skillPath, frontmatter + skill.content);
|
|
11896
12010
|
}
|
|
11897
12011
|
return skillPath;
|
|
11898
12012
|
}
|
|
11899
12013
|
function writePersonalLearnedSection(content) {
|
|
11900
|
-
if (!
|
|
12014
|
+
if (!fs39.existsSync(AUTH_DIR)) fs39.mkdirSync(AUTH_DIR, { recursive: true });
|
|
11901
12015
|
return writeLearnedSectionTo(PERSONAL_LEARNINGS_FILE, PERSONAL_LEARNINGS_HEADER, readPersonalLearnings(), content, 384);
|
|
11902
12016
|
}
|
|
11903
12017
|
function addLearning(bullet, scope = "project") {
|
|
@@ -11910,38 +12024,38 @@ function addLearning(bullet, scope = "project") {
|
|
|
11910
12024
|
return { file: LEARNINGS_FILE, added: result.newCount > 0 };
|
|
11911
12025
|
}
|
|
11912
12026
|
function readPersonalLearnings() {
|
|
11913
|
-
if (!
|
|
11914
|
-
const content =
|
|
12027
|
+
if (!fs39.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
|
|
12028
|
+
const content = fs39.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
|
|
11915
12029
|
const bullets = content.split("\n").filter((l) => l.startsWith("- ")).join("\n");
|
|
11916
12030
|
return bullets || null;
|
|
11917
12031
|
}
|
|
11918
12032
|
function readLearnedSection() {
|
|
11919
|
-
if (
|
|
11920
|
-
const content2 =
|
|
12033
|
+
if (fs39.existsSync(LEARNINGS_FILE)) {
|
|
12034
|
+
const content2 = fs39.readFileSync(LEARNINGS_FILE, "utf-8");
|
|
11921
12035
|
const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
|
|
11922
12036
|
return bullets || null;
|
|
11923
12037
|
}
|
|
11924
12038
|
const claudeMdPath = "CLAUDE.md";
|
|
11925
|
-
if (!
|
|
11926
|
-
const content =
|
|
12039
|
+
if (!fs39.existsSync(claudeMdPath)) return null;
|
|
12040
|
+
const content = fs39.readFileSync(claudeMdPath, "utf-8");
|
|
11927
12041
|
const startIdx = content.indexOf(LEARNED_START);
|
|
11928
12042
|
const endIdx = content.indexOf(LEARNED_END);
|
|
11929
12043
|
if (startIdx === -1 || endIdx === -1) return null;
|
|
11930
12044
|
return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
|
|
11931
12045
|
}
|
|
11932
12046
|
function migrateInlineLearnings() {
|
|
11933
|
-
if (
|
|
12047
|
+
if (fs39.existsSync(LEARNINGS_FILE)) return false;
|
|
11934
12048
|
const claudeMdPath = "CLAUDE.md";
|
|
11935
|
-
if (!
|
|
11936
|
-
const content =
|
|
12049
|
+
if (!fs39.existsSync(claudeMdPath)) return false;
|
|
12050
|
+
const content = fs39.readFileSync(claudeMdPath, "utf-8");
|
|
11937
12051
|
const startIdx = content.indexOf(LEARNED_START);
|
|
11938
12052
|
const endIdx = content.indexOf(LEARNED_END);
|
|
11939
12053
|
if (startIdx === -1 || endIdx === -1) return false;
|
|
11940
12054
|
const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
|
|
11941
12055
|
if (!section) return false;
|
|
11942
|
-
|
|
12056
|
+
fs39.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
|
|
11943
12057
|
const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
|
|
11944
|
-
|
|
12058
|
+
fs39.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
|
|
11945
12059
|
return true;
|
|
11946
12060
|
}
|
|
11947
12061
|
|
|
@@ -11951,7 +12065,7 @@ init_resolve_caliber();
|
|
|
11951
12065
|
init_builtin_skills();
|
|
11952
12066
|
|
|
11953
12067
|
// src/lib/config-discovery.ts
|
|
11954
|
-
import
|
|
12068
|
+
import fs40 from "fs";
|
|
11955
12069
|
import path32 from "path";
|
|
11956
12070
|
var CONFIG_FILE_MARKERS = [
|
|
11957
12071
|
"CLAUDE.md",
|
|
@@ -11978,11 +12092,11 @@ var IGNORE_DIRS3 = /* @__PURE__ */ new Set([
|
|
|
11978
12092
|
var MAX_DEPTH = 4;
|
|
11979
12093
|
function hasConfigFiles(dir) {
|
|
11980
12094
|
for (const marker of CONFIG_FILE_MARKERS) {
|
|
11981
|
-
if (
|
|
12095
|
+
if (fs40.existsSync(path32.join(dir, marker))) return true;
|
|
11982
12096
|
}
|
|
11983
12097
|
for (const marker of CONFIG_DIR_MARKERS) {
|
|
11984
12098
|
const markerPath = path32.join(dir, marker);
|
|
11985
|
-
if (
|
|
12099
|
+
if (fs40.existsSync(markerPath) && fs40.statSync(markerPath).isDirectory()) return true;
|
|
11986
12100
|
}
|
|
11987
12101
|
return false;
|
|
11988
12102
|
}
|
|
@@ -11999,7 +12113,7 @@ function walkForConfigs(baseDir, currentDir, depth, result) {
|
|
|
11999
12113
|
if (depth >= MAX_DEPTH) return;
|
|
12000
12114
|
let entries;
|
|
12001
12115
|
try {
|
|
12002
|
-
entries =
|
|
12116
|
+
entries = fs40.readdirSync(currentDir, { withFileTypes: true });
|
|
12003
12117
|
} catch {
|
|
12004
12118
|
return;
|
|
12005
12119
|
}
|
|
@@ -12018,8 +12132,8 @@ function walkForConfigs(baseDir, currentDir, depth, result) {
|
|
|
12018
12132
|
// src/commands/refresh.ts
|
|
12019
12133
|
function writeRefreshError(error) {
|
|
12020
12134
|
try {
|
|
12021
|
-
if (!
|
|
12022
|
-
|
|
12135
|
+
if (!fs42.existsSync(CALIBER_DIR)) fs42.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
12136
|
+
fs42.writeFileSync(
|
|
12023
12137
|
REFRESH_LAST_ERROR_FILE,
|
|
12024
12138
|
JSON.stringify(
|
|
12025
12139
|
{
|
|
@@ -12038,15 +12152,15 @@ function writeRefreshError(error) {
|
|
|
12038
12152
|
}
|
|
12039
12153
|
function readRefreshError() {
|
|
12040
12154
|
try {
|
|
12041
|
-
if (!
|
|
12042
|
-
return JSON.parse(
|
|
12155
|
+
if (!fs42.existsSync(REFRESH_LAST_ERROR_FILE)) return null;
|
|
12156
|
+
return JSON.parse(fs42.readFileSync(REFRESH_LAST_ERROR_FILE, "utf-8"));
|
|
12043
12157
|
} catch {
|
|
12044
12158
|
return null;
|
|
12045
12159
|
}
|
|
12046
12160
|
}
|
|
12047
12161
|
function clearRefreshError() {
|
|
12048
12162
|
try {
|
|
12049
|
-
if (
|
|
12163
|
+
if (fs42.existsSync(REFRESH_LAST_ERROR_FILE)) fs42.unlinkSync(REFRESH_LAST_ERROR_FILE);
|
|
12050
12164
|
} catch {
|
|
12051
12165
|
}
|
|
12052
12166
|
}
|
|
@@ -12066,11 +12180,11 @@ function log2(quiet, ...args) {
|
|
|
12066
12180
|
function discoverGitRepos(parentDir) {
|
|
12067
12181
|
const repos = [];
|
|
12068
12182
|
try {
|
|
12069
|
-
const entries =
|
|
12183
|
+
const entries = fs42.readdirSync(parentDir, { withFileTypes: true });
|
|
12070
12184
|
for (const entry of entries) {
|
|
12071
12185
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
12072
12186
|
const childPath = path34.join(parentDir, entry.name);
|
|
12073
|
-
if (
|
|
12187
|
+
if (fs42.existsSync(path34.join(childPath, ".git"))) {
|
|
12074
12188
|
repos.push(childPath);
|
|
12075
12189
|
}
|
|
12076
12190
|
}
|
|
@@ -12101,12 +12215,15 @@ function collectFilesToWrite(updatedDocs, dir = ".") {
|
|
|
12101
12215
|
return files;
|
|
12102
12216
|
}
|
|
12103
12217
|
var REFRESH_COOLDOWN_MS = 3e4;
|
|
12218
|
+
var PARALLEL_DIR_CONCURRENCY = 4;
|
|
12104
12219
|
async function refreshDir(repoDir, dir, diff, options) {
|
|
12105
12220
|
const quiet = !!options.quiet;
|
|
12221
|
+
const suppress = !!options.suppressSpinner;
|
|
12222
|
+
const effectiveQuiet = quiet || suppress;
|
|
12106
12223
|
const prefix = options.label ? `${chalk19.bold(options.label)} ` : "";
|
|
12107
12224
|
const absDir = dir === "." ? repoDir : path34.resolve(repoDir, dir);
|
|
12108
12225
|
const scope = dir === "." ? void 0 : dir;
|
|
12109
|
-
const spinner =
|
|
12226
|
+
const spinner = effectiveQuiet ? null : ora6(`${prefix}Analyzing changes...`).start();
|
|
12110
12227
|
const learnedSection = readLearnedSection();
|
|
12111
12228
|
const fingerprint = await collectFingerprint(absDir);
|
|
12112
12229
|
const existingDocs = fingerprint.existingConfigs;
|
|
@@ -12155,7 +12272,7 @@ async function refreshDir(repoDir, dir, diff, options) {
|
|
|
12155
12272
|
}
|
|
12156
12273
|
if (!response.docsUpdated || response.docsUpdated.length === 0) {
|
|
12157
12274
|
spinner?.succeed(`${prefix}No doc updates needed`);
|
|
12158
|
-
return { written: [] };
|
|
12275
|
+
return { written: [], fileChanges: [], syncedAgents: [], changesSummary: null };
|
|
12159
12276
|
}
|
|
12160
12277
|
if (options.dryRun) {
|
|
12161
12278
|
spinner?.info(`${prefix}Dry run \u2014 would update:`);
|
|
@@ -12166,14 +12283,14 @@ async function refreshDir(repoDir, dir, diff, options) {
|
|
|
12166
12283
|
console.log(chalk19.dim(`
|
|
12167
12284
|
${response.changesSummary}`));
|
|
12168
12285
|
}
|
|
12169
|
-
return { written: [] };
|
|
12286
|
+
return { written: [], fileChanges: [], syncedAgents: [], changesSummary: null };
|
|
12170
12287
|
}
|
|
12171
12288
|
const allFilesToWrite = collectFilesToWrite(response.updatedDocs, dir);
|
|
12172
12289
|
const preRefreshContents = /* @__PURE__ */ new Map();
|
|
12173
12290
|
for (const filePath of allFilesToWrite) {
|
|
12174
12291
|
const fullPath = path34.resolve(repoDir, filePath);
|
|
12175
12292
|
try {
|
|
12176
|
-
preRefreshContents.set(filePath,
|
|
12293
|
+
preRefreshContents.set(filePath, fs42.readFileSync(fullPath, "utf-8"));
|
|
12177
12294
|
} catch {
|
|
12178
12295
|
preRefreshContents.set(filePath, null);
|
|
12179
12296
|
}
|
|
@@ -12192,43 +12309,47 @@ async function refreshDir(repoDir, dir, diff, options) {
|
|
|
12192
12309
|
const fullPath = path34.resolve(repoDir, filePath);
|
|
12193
12310
|
if (content === null) {
|
|
12194
12311
|
try {
|
|
12195
|
-
|
|
12312
|
+
fs42.unlinkSync(fullPath);
|
|
12196
12313
|
} catch {
|
|
12197
12314
|
}
|
|
12198
12315
|
} else {
|
|
12199
|
-
|
|
12316
|
+
fs42.writeFileSync(fullPath, content);
|
|
12200
12317
|
}
|
|
12201
12318
|
}
|
|
12202
12319
|
spinner?.warn(
|
|
12203
12320
|
`${prefix}Refresh reverted \u2014 score would drop from ${preScore.score} to ${postScore.score}`
|
|
12204
12321
|
);
|
|
12205
|
-
log2(
|
|
12206
|
-
|
|
12322
|
+
log2(
|
|
12323
|
+
effectiveQuiet,
|
|
12324
|
+
chalk19.dim(` Config quality gate prevented a regression. No files were changed.`)
|
|
12325
|
+
);
|
|
12326
|
+
return { written: [], fileChanges: [], syncedAgents: [], changesSummary: null };
|
|
12207
12327
|
}
|
|
12208
12328
|
recordScore(postScore, "refresh");
|
|
12209
12329
|
}
|
|
12210
12330
|
spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
|
|
12211
|
-
const
|
|
12212
|
-
|
|
12213
|
-
|
|
12214
|
-
|
|
12215
|
-
|
|
12216
|
-
|
|
12217
|
-
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
|
|
12221
|
-
|
|
12222
|
-
|
|
12223
|
-
|
|
12224
|
-
|
|
12225
|
-
|
|
12226
|
-
|
|
12227
|
-
|
|
12228
|
-
|
|
12331
|
+
const fileChanges = response.fileChanges || [];
|
|
12332
|
+
const fileChangesMap = new Map(fileChanges.map((fc) => [fc.file, fc.description]));
|
|
12333
|
+
const syncedAgents = detectSyncedAgents(written);
|
|
12334
|
+
if (!suppress) {
|
|
12335
|
+
for (const file of written) {
|
|
12336
|
+
const desc = fileChangesMap.get(file);
|
|
12337
|
+
const suffix = desc ? chalk19.dim(` \u2014 ${desc}`) : "";
|
|
12338
|
+
log2(effectiveQuiet, ` ${chalk19.green("\u2713")} ${file}${suffix}`);
|
|
12339
|
+
}
|
|
12340
|
+
if (syncedAgents.length > 1) {
|
|
12341
|
+
log2(
|
|
12342
|
+
effectiveQuiet,
|
|
12343
|
+
chalk19.cyan(`
|
|
12344
|
+
${syncedAgents.length} agent formats in sync (${syncedAgents.join(", ")})`)
|
|
12345
|
+
);
|
|
12346
|
+
}
|
|
12347
|
+
if (response.changesSummary) {
|
|
12348
|
+
log2(effectiveQuiet, chalk19.dim(`
|
|
12229
12349
|
${response.changesSummary}`));
|
|
12350
|
+
}
|
|
12230
12351
|
}
|
|
12231
|
-
return { written };
|
|
12352
|
+
return { written, fileChanges, syncedAgents, changesSummary: response.changesSummary };
|
|
12232
12353
|
}
|
|
12233
12354
|
async function refreshSingleRepo(repoDir, options) {
|
|
12234
12355
|
const quiet = !!options.quiet;
|
|
@@ -12260,21 +12381,57 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
12260
12381
|
} else {
|
|
12261
12382
|
log2(quiet, chalk19.dim(`${prefix}Found configs in ${configDirs.length} directories
|
|
12262
12383
|
`));
|
|
12384
|
+
const dirsWithChanges = configDirs.map((dir) => ({ dir, scopedDiff: scopeDiffToDir(diff, dir, configDirs) })).filter(({ scopedDiff }) => scopedDiff.hasChanges);
|
|
12385
|
+
const parallelSpinner = quiet ? null : ora6(
|
|
12386
|
+
`${prefix}Refreshing ${dirsWithChanges.length} director${dirsWithChanges.length === 1 ? "y" : "ies"}...`
|
|
12387
|
+
).start();
|
|
12388
|
+
const limit = pLimit(PARALLEL_DIR_CONCURRENCY);
|
|
12389
|
+
const results = await Promise.allSettled(
|
|
12390
|
+
dirsWithChanges.map(({ dir, scopedDiff }) => {
|
|
12391
|
+
const dirLabel = dir === "." ? "root" : dir;
|
|
12392
|
+
return limit(
|
|
12393
|
+
() => refreshDir(repoDir, dir, scopedDiff, {
|
|
12394
|
+
...options,
|
|
12395
|
+
suppressSpinner: true,
|
|
12396
|
+
label: dirLabel
|
|
12397
|
+
})
|
|
12398
|
+
);
|
|
12399
|
+
})
|
|
12400
|
+
);
|
|
12401
|
+
parallelSpinner?.stop();
|
|
12263
12402
|
let hadFailure = false;
|
|
12264
|
-
for (const
|
|
12265
|
-
const
|
|
12266
|
-
if (!scopedDiff.hasChanges) continue;
|
|
12403
|
+
for (const [i, result] of results.entries()) {
|
|
12404
|
+
const { dir } = dirsWithChanges[i];
|
|
12267
12405
|
const dirLabel = dir === "." ? "root" : dir;
|
|
12268
|
-
|
|
12269
|
-
await refreshDir(repoDir, dir, scopedDiff, { ...options, label: dirLabel });
|
|
12270
|
-
} catch (err) {
|
|
12406
|
+
if (result.status === "rejected") {
|
|
12271
12407
|
hadFailure = true;
|
|
12272
12408
|
log2(
|
|
12273
12409
|
quiet,
|
|
12274
12410
|
chalk19.yellow(
|
|
12275
|
-
` ${dirLabel}: refresh failed \u2014 ${
|
|
12411
|
+
` ${dirLabel}: refresh failed \u2014 ${result.reason instanceof Error ? result.reason.message : "unknown error"}`
|
|
12276
12412
|
)
|
|
12277
12413
|
);
|
|
12414
|
+
} else {
|
|
12415
|
+
const { written, fileChanges, syncedAgents, changesSummary } = result.value;
|
|
12416
|
+
const fileChangesMap = new Map(fileChanges.map((fc) => [fc.file, fc.description]));
|
|
12417
|
+
for (const file of written) {
|
|
12418
|
+
const desc = fileChangesMap.get(file);
|
|
12419
|
+
const suffix = desc ? chalk19.dim(` \u2014 ${desc}`) : "";
|
|
12420
|
+
log2(quiet, ` ${chalk19.green("\u2713")} ${dirLabel}/${file}${suffix}`);
|
|
12421
|
+
}
|
|
12422
|
+
if (syncedAgents.length > 1) {
|
|
12423
|
+
log2(
|
|
12424
|
+
quiet,
|
|
12425
|
+
chalk19.cyan(
|
|
12426
|
+
`
|
|
12427
|
+
${syncedAgents.length} agent formats in sync (${syncedAgents.join(", ")})`
|
|
12428
|
+
)
|
|
12429
|
+
);
|
|
12430
|
+
}
|
|
12431
|
+
if (changesSummary) {
|
|
12432
|
+
log2(quiet, chalk19.dim(`
|
|
12433
|
+
${changesSummary}`));
|
|
12434
|
+
}
|
|
12278
12435
|
}
|
|
12279
12436
|
}
|
|
12280
12437
|
if (hadFailure) {
|
|
@@ -12366,7 +12523,7 @@ async function refreshCommand(options) {
|
|
|
12366
12523
|
|
|
12367
12524
|
// src/commands/hooks.ts
|
|
12368
12525
|
import chalk20 from "chalk";
|
|
12369
|
-
import
|
|
12526
|
+
import fs43 from "fs";
|
|
12370
12527
|
var HOOKS = [
|
|
12371
12528
|
{
|
|
12372
12529
|
id: "session-end",
|
|
@@ -12430,11 +12587,11 @@ async function hooksCommand(options) {
|
|
|
12430
12587
|
console.log(chalk20.green(" \u2713") + ` ${hook.label} enabled`);
|
|
12431
12588
|
}
|
|
12432
12589
|
}
|
|
12433
|
-
if (
|
|
12590
|
+
if (fs43.existsSync(".claude")) {
|
|
12434
12591
|
const r = installLearningHooks();
|
|
12435
12592
|
if (r.installed) console.log(chalk20.green(" \u2713") + " Claude Code learning hooks enabled");
|
|
12436
12593
|
}
|
|
12437
|
-
if (
|
|
12594
|
+
if (fs43.existsSync(".cursor")) {
|
|
12438
12595
|
const r = installCursorLearningHooks();
|
|
12439
12596
|
if (r.installed) console.log(chalk20.green(" \u2713") + " Cursor learning hooks enabled");
|
|
12440
12597
|
}
|
|
@@ -12602,7 +12759,7 @@ async function configCommand() {
|
|
|
12602
12759
|
}
|
|
12603
12760
|
|
|
12604
12761
|
// src/commands/learn.ts
|
|
12605
|
-
import
|
|
12762
|
+
import fs47 from "fs";
|
|
12606
12763
|
import path38 from "path";
|
|
12607
12764
|
import chalk23 from "chalk";
|
|
12608
12765
|
|
|
@@ -12634,7 +12791,7 @@ function readStdin() {
|
|
|
12634
12791
|
}
|
|
12635
12792
|
|
|
12636
12793
|
// src/learner/storage.ts
|
|
12637
|
-
import
|
|
12794
|
+
import fs44 from "fs";
|
|
12638
12795
|
import path35 from "path";
|
|
12639
12796
|
var MAX_RESPONSE_LENGTH = 2e3;
|
|
12640
12797
|
var MAX_PROMPT_LENGTH = 2e3;
|
|
@@ -12646,8 +12803,8 @@ var DEFAULT_STATE = {
|
|
|
12646
12803
|
lastAnalysisEventCount: 0
|
|
12647
12804
|
};
|
|
12648
12805
|
function ensureLearningDir() {
|
|
12649
|
-
if (!
|
|
12650
|
-
|
|
12806
|
+
if (!fs44.existsSync(getLearningDir())) {
|
|
12807
|
+
fs44.mkdirSync(getLearningDir(), { recursive: true });
|
|
12651
12808
|
}
|
|
12652
12809
|
}
|
|
12653
12810
|
function sessionFilePath() {
|
|
@@ -12663,9 +12820,9 @@ function truncateResponse(response) {
|
|
|
12663
12820
|
}
|
|
12664
12821
|
function trimSessionFileIfNeeded(filePath) {
|
|
12665
12822
|
try {
|
|
12666
|
-
const stat =
|
|
12823
|
+
const stat = fs44.statSync(filePath);
|
|
12667
12824
|
if (stat.size > MAX_SESSION_FILE_BYTES) {
|
|
12668
|
-
|
|
12825
|
+
fs44.writeFileSync(filePath, "");
|
|
12669
12826
|
resetState();
|
|
12670
12827
|
return;
|
|
12671
12828
|
}
|
|
@@ -12674,10 +12831,10 @@ function trimSessionFileIfNeeded(filePath) {
|
|
|
12674
12831
|
}
|
|
12675
12832
|
const state = readState2();
|
|
12676
12833
|
if (state.eventCount + 1 > LEARNING_MAX_EVENTS) {
|
|
12677
|
-
const lines =
|
|
12834
|
+
const lines = fs44.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
12678
12835
|
if (lines.length > LEARNING_MAX_EVENTS) {
|
|
12679
12836
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
12680
|
-
|
|
12837
|
+
fs44.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
12681
12838
|
}
|
|
12682
12839
|
}
|
|
12683
12840
|
}
|
|
@@ -12685,7 +12842,7 @@ function appendEvent(event) {
|
|
|
12685
12842
|
ensureLearningDir();
|
|
12686
12843
|
const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
|
|
12687
12844
|
const filePath = sessionFilePath();
|
|
12688
|
-
|
|
12845
|
+
fs44.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
12689
12846
|
trimSessionFileIfNeeded(filePath);
|
|
12690
12847
|
}
|
|
12691
12848
|
function appendPromptEvent(event) {
|
|
@@ -12695,22 +12852,22 @@ function appendPromptEvent(event) {
|
|
|
12695
12852
|
prompt_content: event.prompt_content.length > MAX_PROMPT_LENGTH ? event.prompt_content.slice(0, MAX_PROMPT_LENGTH) : event.prompt_content
|
|
12696
12853
|
};
|
|
12697
12854
|
const filePath = sessionFilePath();
|
|
12698
|
-
|
|
12855
|
+
fs44.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
12699
12856
|
trimSessionFileIfNeeded(filePath);
|
|
12700
12857
|
}
|
|
12701
12858
|
function readAllEvents() {
|
|
12702
12859
|
const filePath = sessionFilePath();
|
|
12703
12860
|
try {
|
|
12704
|
-
const stat =
|
|
12861
|
+
const stat = fs44.statSync(filePath);
|
|
12705
12862
|
if (stat.size > MAX_SESSION_FILE_BYTES) {
|
|
12706
|
-
|
|
12863
|
+
fs44.writeFileSync(filePath, "");
|
|
12707
12864
|
resetState();
|
|
12708
12865
|
return [];
|
|
12709
12866
|
}
|
|
12710
12867
|
} catch {
|
|
12711
12868
|
return [];
|
|
12712
12869
|
}
|
|
12713
|
-
const lines =
|
|
12870
|
+
const lines = fs44.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
12714
12871
|
const events = [];
|
|
12715
12872
|
for (const line of lines) {
|
|
12716
12873
|
try {
|
|
@@ -12723,33 +12880,33 @@ function readAllEvents() {
|
|
|
12723
12880
|
function getEventCount() {
|
|
12724
12881
|
const filePath = sessionFilePath();
|
|
12725
12882
|
try {
|
|
12726
|
-
const stat =
|
|
12883
|
+
const stat = fs44.statSync(filePath);
|
|
12727
12884
|
if (stat.size > MAX_SESSION_FILE_BYTES) return 0;
|
|
12728
12885
|
} catch {
|
|
12729
12886
|
return 0;
|
|
12730
12887
|
}
|
|
12731
|
-
const content =
|
|
12888
|
+
const content = fs44.readFileSync(filePath, "utf-8");
|
|
12732
12889
|
return content.split("\n").filter(Boolean).length;
|
|
12733
12890
|
}
|
|
12734
12891
|
function clearSession() {
|
|
12735
12892
|
const filePath = sessionFilePath();
|
|
12736
12893
|
try {
|
|
12737
|
-
|
|
12894
|
+
fs44.writeFileSync(filePath, "");
|
|
12738
12895
|
} catch {
|
|
12739
12896
|
}
|
|
12740
12897
|
}
|
|
12741
12898
|
function readState2() {
|
|
12742
12899
|
const filePath = stateFilePath();
|
|
12743
|
-
if (!
|
|
12900
|
+
if (!fs44.existsSync(filePath)) return { ...DEFAULT_STATE };
|
|
12744
12901
|
try {
|
|
12745
|
-
return JSON.parse(
|
|
12902
|
+
return JSON.parse(fs44.readFileSync(filePath, "utf-8"));
|
|
12746
12903
|
} catch {
|
|
12747
12904
|
return { ...DEFAULT_STATE };
|
|
12748
12905
|
}
|
|
12749
12906
|
}
|
|
12750
12907
|
function writeState2(state) {
|
|
12751
12908
|
ensureLearningDir();
|
|
12752
|
-
|
|
12909
|
+
fs44.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
|
|
12753
12910
|
}
|
|
12754
12911
|
function resetState() {
|
|
12755
12912
|
writeState2({ ...DEFAULT_STATE });
|
|
@@ -12762,11 +12919,11 @@ function lockFilePath() {
|
|
|
12762
12919
|
function acquireFinalizeLock() {
|
|
12763
12920
|
ensureLearningDir();
|
|
12764
12921
|
const lockPath = lockFilePath();
|
|
12765
|
-
if (
|
|
12922
|
+
if (fs44.existsSync(lockPath)) {
|
|
12766
12923
|
try {
|
|
12767
|
-
const stat =
|
|
12924
|
+
const stat = fs44.statSync(lockPath);
|
|
12768
12925
|
if (Date.now() - stat.mtimeMs < LOCK_STALE_MS) {
|
|
12769
|
-
const pid = parseInt(
|
|
12926
|
+
const pid = parseInt(fs44.readFileSync(lockPath, "utf-8").trim(), 10);
|
|
12770
12927
|
if (!isNaN(pid) && isProcessAlive(pid)) {
|
|
12771
12928
|
return false;
|
|
12772
12929
|
}
|
|
@@ -12774,12 +12931,12 @@ function acquireFinalizeLock() {
|
|
|
12774
12931
|
} catch {
|
|
12775
12932
|
}
|
|
12776
12933
|
try {
|
|
12777
|
-
|
|
12934
|
+
fs44.unlinkSync(lockPath);
|
|
12778
12935
|
} catch {
|
|
12779
12936
|
}
|
|
12780
12937
|
}
|
|
12781
12938
|
try {
|
|
12782
|
-
|
|
12939
|
+
fs44.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
12783
12940
|
return true;
|
|
12784
12941
|
} catch {
|
|
12785
12942
|
return false;
|
|
@@ -12796,13 +12953,13 @@ function isProcessAlive(pid) {
|
|
|
12796
12953
|
function releaseFinalizeLock() {
|
|
12797
12954
|
const lockPath = lockFilePath();
|
|
12798
12955
|
try {
|
|
12799
|
-
if (
|
|
12956
|
+
if (fs44.existsSync(lockPath)) fs44.unlinkSync(lockPath);
|
|
12800
12957
|
} catch {
|
|
12801
12958
|
}
|
|
12802
12959
|
}
|
|
12803
12960
|
|
|
12804
12961
|
// src/lib/notifications.ts
|
|
12805
|
-
import
|
|
12962
|
+
import fs45 from "fs";
|
|
12806
12963
|
import path36 from "path";
|
|
12807
12964
|
import chalk22 from "chalk";
|
|
12808
12965
|
function notificationFilePath() {
|
|
@@ -12811,15 +12968,15 @@ function notificationFilePath() {
|
|
|
12811
12968
|
function writeFinalizeSummary(summary) {
|
|
12812
12969
|
try {
|
|
12813
12970
|
ensureLearningDir();
|
|
12814
|
-
|
|
12971
|
+
fs45.writeFileSync(notificationFilePath(), JSON.stringify(summary, null, 2));
|
|
12815
12972
|
} catch {
|
|
12816
12973
|
}
|
|
12817
12974
|
}
|
|
12818
12975
|
function checkPendingNotifications() {
|
|
12819
12976
|
try {
|
|
12820
|
-
if (!
|
|
12821
|
-
const raw =
|
|
12822
|
-
|
|
12977
|
+
if (!fs45.existsSync(notificationFilePath())) return;
|
|
12978
|
+
const raw = fs45.readFileSync(notificationFilePath(), "utf-8");
|
|
12979
|
+
fs45.unlinkSync(notificationFilePath());
|
|
12823
12980
|
const summary = JSON.parse(raw);
|
|
12824
12981
|
if (!summary.newItemCount || summary.newItemCount === 0) return;
|
|
12825
12982
|
const wasteLabel = summary.wasteTokens > 0 ? ` (~${summary.wasteTokens.toLocaleString()} wasted tokens captured)` : "";
|
|
@@ -12835,7 +12992,7 @@ function checkPendingNotifications() {
|
|
|
12835
12992
|
console.log("");
|
|
12836
12993
|
} catch {
|
|
12837
12994
|
try {
|
|
12838
|
-
|
|
12995
|
+
fs45.unlinkSync(notificationFilePath());
|
|
12839
12996
|
} catch {
|
|
12840
12997
|
}
|
|
12841
12998
|
}
|
|
@@ -12987,7 +13144,7 @@ function calculateSessionWaste(events) {
|
|
|
12987
13144
|
init_config();
|
|
12988
13145
|
|
|
12989
13146
|
// src/learner/roi.ts
|
|
12990
|
-
import
|
|
13147
|
+
import fs46 from "fs";
|
|
12991
13148
|
import path37 from "path";
|
|
12992
13149
|
var DEFAULT_TOTALS = {
|
|
12993
13150
|
totalWasteTokens: 0,
|
|
@@ -13006,15 +13163,15 @@ function roiFilePath() {
|
|
|
13006
13163
|
}
|
|
13007
13164
|
function readROIStats() {
|
|
13008
13165
|
const filePath = roiFilePath();
|
|
13009
|
-
if (!
|
|
13166
|
+
if (!fs46.existsSync(filePath)) {
|
|
13010
13167
|
return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
|
|
13011
13168
|
}
|
|
13012
13169
|
try {
|
|
13013
|
-
return JSON.parse(
|
|
13170
|
+
return JSON.parse(fs46.readFileSync(filePath, "utf-8"));
|
|
13014
13171
|
} catch {
|
|
13015
13172
|
try {
|
|
13016
13173
|
const corruptPath = filePath + ".corrupt";
|
|
13017
|
-
|
|
13174
|
+
fs46.renameSync(filePath, corruptPath);
|
|
13018
13175
|
console.error(`caliber: roi-stats.json was corrupt \u2014 renamed to ${corruptPath}`);
|
|
13019
13176
|
} catch {
|
|
13020
13177
|
}
|
|
@@ -13023,7 +13180,7 @@ function readROIStats() {
|
|
|
13023
13180
|
}
|
|
13024
13181
|
function writeROIStats(stats) {
|
|
13025
13182
|
ensureLearningDir();
|
|
13026
|
-
|
|
13183
|
+
fs46.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
|
|
13027
13184
|
}
|
|
13028
13185
|
function recalculateTotals(stats) {
|
|
13029
13186
|
const totals = stats.totals;
|
|
@@ -13233,20 +13390,27 @@ var INCREMENTAL_INTERVAL = 50;
|
|
|
13233
13390
|
function writeFinalizeError(message) {
|
|
13234
13391
|
try {
|
|
13235
13392
|
const errorPath = path38.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
|
|
13236
|
-
if (!
|
|
13237
|
-
|
|
13238
|
-
|
|
13239
|
-
|
|
13240
|
-
|
|
13241
|
-
|
|
13393
|
+
if (!fs47.existsSync(getLearningDir())) fs47.mkdirSync(getLearningDir(), { recursive: true });
|
|
13394
|
+
fs47.writeFileSync(
|
|
13395
|
+
errorPath,
|
|
13396
|
+
JSON.stringify(
|
|
13397
|
+
{
|
|
13398
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13399
|
+
error: message,
|
|
13400
|
+
pid: process.pid
|
|
13401
|
+
},
|
|
13402
|
+
null,
|
|
13403
|
+
2
|
|
13404
|
+
)
|
|
13405
|
+
);
|
|
13242
13406
|
} catch {
|
|
13243
13407
|
}
|
|
13244
13408
|
}
|
|
13245
13409
|
function readFinalizeError() {
|
|
13246
13410
|
try {
|
|
13247
13411
|
const errorPath = path38.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
|
|
13248
|
-
if (!
|
|
13249
|
-
return JSON.parse(
|
|
13412
|
+
if (!fs47.existsSync(errorPath)) return null;
|
|
13413
|
+
return JSON.parse(fs47.readFileSync(errorPath, "utf-8"));
|
|
13250
13414
|
} catch {
|
|
13251
13415
|
return null;
|
|
13252
13416
|
}
|
|
@@ -13294,17 +13458,19 @@ async function learnObserveCommand(options) {
|
|
|
13294
13458
|
const eventsSinceLastAnalysis = state.eventCount - (state.lastAnalysisEventCount || 0);
|
|
13295
13459
|
if (eventsSinceLastAnalysis >= INCREMENTAL_INTERVAL) {
|
|
13296
13460
|
try {
|
|
13297
|
-
const { resolveCaliber: resolveCaliber2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
|
|
13461
|
+
const { resolveCaliber: resolveCaliber2, isNpxResolution: isNpxResolution2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
|
|
13298
13462
|
const bin = resolveCaliber2();
|
|
13299
13463
|
const { spawn: spawn4 } = await import("child_process");
|
|
13300
13464
|
const logPath = path38.join(getLearningDir(), LEARNING_FINALIZE_LOG);
|
|
13301
|
-
if (!
|
|
13302
|
-
const logFd =
|
|
13303
|
-
|
|
13465
|
+
if (!fs47.existsSync(getLearningDir())) fs47.mkdirSync(getLearningDir(), { recursive: true });
|
|
13466
|
+
const logFd = fs47.openSync(logPath, "a");
|
|
13467
|
+
const NPX_SUFFIX = " --yes @rely-ai/caliber";
|
|
13468
|
+
const [exe, binArgs] = isNpxResolution2() ? [bin.slice(0, -NPX_SUFFIX.length) || "npx", ["--yes", "@rely-ai/caliber"]] : [bin, []];
|
|
13469
|
+
spawn4(exe, [...binArgs, "learn", "finalize", "--auto", "--incremental"], {
|
|
13304
13470
|
detached: true,
|
|
13305
13471
|
stdio: ["ignore", logFd, logFd]
|
|
13306
13472
|
}).unref();
|
|
13307
|
-
|
|
13473
|
+
fs47.closeSync(logFd);
|
|
13308
13474
|
} catch {
|
|
13309
13475
|
}
|
|
13310
13476
|
}
|
|
@@ -13317,7 +13483,8 @@ async function learnFinalizeCommand(options) {
|
|
|
13317
13483
|
if (!options?.force && !isAuto) {
|
|
13318
13484
|
const { isCaliberRunning: isCaliberRunning2 } = await Promise.resolve().then(() => (init_lock(), lock_exports));
|
|
13319
13485
|
if (isCaliberRunning2()) {
|
|
13320
|
-
if (!isAuto)
|
|
13486
|
+
if (!isAuto)
|
|
13487
|
+
console.log(chalk23.dim("caliber: skipping finalize \u2014 another caliber process is running"));
|
|
13321
13488
|
return;
|
|
13322
13489
|
}
|
|
13323
13490
|
}
|
|
@@ -13325,7 +13492,8 @@ async function learnFinalizeCommand(options) {
|
|
|
13325
13492
|
await new Promise((r) => setTimeout(r, AUTO_SETTLE_MS));
|
|
13326
13493
|
}
|
|
13327
13494
|
if (!acquireFinalizeLock()) {
|
|
13328
|
-
if (!isAuto)
|
|
13495
|
+
if (!isAuto)
|
|
13496
|
+
console.log(chalk23.dim("caliber: skipping finalize \u2014 another finalize is in progress"));
|
|
13329
13497
|
return;
|
|
13330
13498
|
}
|
|
13331
13499
|
let analyzed = false;
|
|
@@ -13333,7 +13501,11 @@ async function learnFinalizeCommand(options) {
|
|
|
13333
13501
|
const config = loadConfig();
|
|
13334
13502
|
if (!config) {
|
|
13335
13503
|
if (isAuto) return;
|
|
13336
|
-
console.log(
|
|
13504
|
+
console.log(
|
|
13505
|
+
chalk23.yellow(
|
|
13506
|
+
`caliber: no LLM provider configured \u2014 run \`${resolveCaliber()} config\` first`
|
|
13507
|
+
)
|
|
13508
|
+
);
|
|
13337
13509
|
clearSession();
|
|
13338
13510
|
resetState();
|
|
13339
13511
|
return;
|
|
@@ -13341,7 +13513,12 @@ async function learnFinalizeCommand(options) {
|
|
|
13341
13513
|
const allEvents = readAllEvents();
|
|
13342
13514
|
const threshold = isAuto ? MIN_EVENTS_AUTO : MIN_EVENTS_FOR_ANALYSIS;
|
|
13343
13515
|
if (allEvents.length < threshold) {
|
|
13344
|
-
if (!isAuto)
|
|
13516
|
+
if (!isAuto)
|
|
13517
|
+
console.log(
|
|
13518
|
+
chalk23.dim(
|
|
13519
|
+
`caliber: ${allEvents.length}/${threshold} events recorded \u2014 need more before analysis`
|
|
13520
|
+
)
|
|
13521
|
+
);
|
|
13345
13522
|
return;
|
|
13346
13523
|
}
|
|
13347
13524
|
await validateModel({ fast: true });
|
|
@@ -13350,7 +13527,12 @@ async function learnFinalizeCommand(options) {
|
|
|
13350
13527
|
const analysisOffset = isIncremental ? state.lastAnalysisEventCount || 0 : 0;
|
|
13351
13528
|
const events = analysisOffset > 0 ? allEvents.slice(analysisOffset) : allEvents;
|
|
13352
13529
|
if (events.length < threshold) {
|
|
13353
|
-
if (!isAuto)
|
|
13530
|
+
if (!isAuto)
|
|
13531
|
+
console.log(
|
|
13532
|
+
chalk23.dim(
|
|
13533
|
+
`caliber: ${events.length}/${threshold} new events since last analysis \u2014 need more`
|
|
13534
|
+
)
|
|
13535
|
+
);
|
|
13354
13536
|
return;
|
|
13355
13537
|
}
|
|
13356
13538
|
const existingConfigs = readExistingConfigs(process.cwd());
|
|
@@ -13386,7 +13568,11 @@ async function learnFinalizeCommand(options) {
|
|
|
13386
13568
|
});
|
|
13387
13569
|
} else {
|
|
13388
13570
|
const wasteLabel = waste.totalWasteTokens > 0 ? ` (~${waste.totalWasteTokens.toLocaleString()} wasted tokens captured)` : "";
|
|
13389
|
-
console.log(
|
|
13571
|
+
console.log(
|
|
13572
|
+
chalk23.dim(
|
|
13573
|
+
`caliber: learned ${result.newItemCount} new pattern${result.newItemCount === 1 ? "" : "s"}${wasteLabel}`
|
|
13574
|
+
)
|
|
13575
|
+
);
|
|
13390
13576
|
for (const item of result.newItems) {
|
|
13391
13577
|
console.log(chalk23.dim(` + ${item.replace(/^- /, "").slice(0, 80)}`));
|
|
13392
13578
|
}
|
|
@@ -13481,12 +13667,20 @@ async function learnFinalizeCommand(options) {
|
|
|
13481
13667
|
if (!isIncremental) {
|
|
13482
13668
|
const staleLearnings = findStaleLearnings(roiStats);
|
|
13483
13669
|
if (staleLearnings.length > 0 && !isAuto) {
|
|
13484
|
-
console.log(
|
|
13670
|
+
console.log(
|
|
13671
|
+
chalk23.yellow(
|
|
13672
|
+
`caliber: ${staleLearnings.length} learning${staleLearnings.length === 1 ? "" : "s"} never activated \u2014 run \`${resolveCaliber()} learn list --verbose\` to review`
|
|
13673
|
+
)
|
|
13674
|
+
);
|
|
13485
13675
|
}
|
|
13486
13676
|
}
|
|
13487
13677
|
if (!isAuto && t.estimatedSavingsTokens > 0) {
|
|
13488
13678
|
const totalLearnings = existingLearnedItems + newLearningsProduced;
|
|
13489
|
-
console.log(
|
|
13679
|
+
console.log(
|
|
13680
|
+
chalk23.dim(
|
|
13681
|
+
`caliber: ${totalLearnings} learnings active \u2014 est. ~${t.estimatedSavingsTokens.toLocaleString()} tokens saved across ${t.totalSessionsWithLearnings} sessions`
|
|
13682
|
+
)
|
|
13683
|
+
);
|
|
13490
13684
|
}
|
|
13491
13685
|
} catch (err) {
|
|
13492
13686
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -13511,7 +13705,7 @@ async function learnFinalizeCommand(options) {
|
|
|
13511
13705
|
}
|
|
13512
13706
|
async function learnInstallCommand() {
|
|
13513
13707
|
let anyInstalled = false;
|
|
13514
|
-
if (
|
|
13708
|
+
if (fs47.existsSync(".claude")) {
|
|
13515
13709
|
const r = installLearningHooks();
|
|
13516
13710
|
if (r.installed) {
|
|
13517
13711
|
console.log(chalk23.green("\u2713") + " Claude Code learning hooks installed");
|
|
@@ -13520,7 +13714,7 @@ async function learnInstallCommand() {
|
|
|
13520
13714
|
console.log(chalk23.dim(" Claude Code hooks already installed"));
|
|
13521
13715
|
}
|
|
13522
13716
|
}
|
|
13523
|
-
if (
|
|
13717
|
+
if (fs47.existsSync(".cursor")) {
|
|
13524
13718
|
const r = installCursorLearningHooks();
|
|
13525
13719
|
if (r.installed) {
|
|
13526
13720
|
console.log(chalk23.green("\u2713") + " Cursor learning hooks installed");
|
|
@@ -13529,13 +13723,19 @@ async function learnInstallCommand() {
|
|
|
13529
13723
|
console.log(chalk23.dim(" Cursor hooks already installed"));
|
|
13530
13724
|
}
|
|
13531
13725
|
}
|
|
13532
|
-
if (!
|
|
13726
|
+
if (!fs47.existsSync(".claude") && !fs47.existsSync(".cursor")) {
|
|
13533
13727
|
console.log(chalk23.yellow("No .claude/ or .cursor/ directory found."));
|
|
13534
|
-
console.log(
|
|
13728
|
+
console.log(
|
|
13729
|
+
chalk23.dim(` Run \`${resolveCaliber()} init\` first, or create the directory manually.`)
|
|
13730
|
+
);
|
|
13535
13731
|
return;
|
|
13536
13732
|
}
|
|
13537
13733
|
if (anyInstalled) {
|
|
13538
|
-
console.log(
|
|
13734
|
+
console.log(
|
|
13735
|
+
chalk23.dim(
|
|
13736
|
+
` Tool usage will be recorded and learnings extracted after \u2265${MIN_EVENTS_FOR_ANALYSIS} events.`
|
|
13737
|
+
)
|
|
13738
|
+
);
|
|
13539
13739
|
console.log(chalk23.dim(" Learnings written to CALIBER_LEARNINGS.md."));
|
|
13540
13740
|
}
|
|
13541
13741
|
}
|
|
@@ -13573,7 +13773,9 @@ async function learnStatusCommand() {
|
|
|
13573
13773
|
console.log(chalk23.dim("\u2717") + " Cursor hooks " + chalk23.dim("not installed"));
|
|
13574
13774
|
}
|
|
13575
13775
|
if (!claudeInstalled && !cursorInstalled) {
|
|
13576
|
-
console.log(
|
|
13776
|
+
console.log(
|
|
13777
|
+
chalk23.dim(` Run \`${resolveCaliber()} learn install\` to enable session learning.`)
|
|
13778
|
+
);
|
|
13577
13779
|
}
|
|
13578
13780
|
console.log();
|
|
13579
13781
|
console.log(`Events recorded: ${chalk23.cyan(String(eventCount))}`);
|
|
@@ -13588,7 +13790,7 @@ async function learnStatusCommand() {
|
|
|
13588
13790
|
console.log(`Last error: ${chalk23.red(lastError.error)}`);
|
|
13589
13791
|
console.log(chalk23.dim(` at ${lastError.timestamp}`));
|
|
13590
13792
|
const logPath = path38.join(getLearningDir(), LEARNING_FINALIZE_LOG);
|
|
13591
|
-
if (
|
|
13793
|
+
if (fs47.existsSync(logPath)) {
|
|
13592
13794
|
console.log(chalk23.dim(` Full log: ${logPath}`));
|
|
13593
13795
|
}
|
|
13594
13796
|
}
|
|
@@ -13657,7 +13859,11 @@ async function learnListCommand(options) {
|
|
|
13657
13859
|
async function learnDeleteCommand(indexStr) {
|
|
13658
13860
|
const index = parseInt(indexStr, 10);
|
|
13659
13861
|
if (isNaN(index) || index < 1) {
|
|
13660
|
-
console.log(
|
|
13862
|
+
console.log(
|
|
13863
|
+
chalk23.red(
|
|
13864
|
+
`Invalid index: "${indexStr}". Use a number from \`${resolveCaliber()} learn list\`.`
|
|
13865
|
+
)
|
|
13866
|
+
);
|
|
13661
13867
|
return;
|
|
13662
13868
|
}
|
|
13663
13869
|
const items = getAllLearnings();
|
|
@@ -13668,11 +13874,11 @@ async function learnDeleteCommand(indexStr) {
|
|
|
13668
13874
|
}
|
|
13669
13875
|
const item = items[targetIdx];
|
|
13670
13876
|
const filePath = item.source === "personal" ? PERSONAL_LEARNINGS_FILE : "CALIBER_LEARNINGS.md";
|
|
13671
|
-
if (!
|
|
13877
|
+
if (!fs47.existsSync(filePath)) {
|
|
13672
13878
|
console.log(chalk23.red("Learnings file not found."));
|
|
13673
13879
|
return;
|
|
13674
13880
|
}
|
|
13675
|
-
const content =
|
|
13881
|
+
const content = fs47.readFileSync(filePath, "utf-8");
|
|
13676
13882
|
const lines = content.split("\n");
|
|
13677
13883
|
const bulletsOfSource = items.filter((i) => i.source === item.source);
|
|
13678
13884
|
const posInFile = bulletsOfSource.indexOf(item);
|
|
@@ -13693,9 +13899,9 @@ async function learnDeleteCommand(indexStr) {
|
|
|
13693
13899
|
}
|
|
13694
13900
|
const bulletToRemove = lines[lineToRemove];
|
|
13695
13901
|
const newLines = lines.filter((_, i) => i !== lineToRemove);
|
|
13696
|
-
|
|
13902
|
+
fs47.writeFileSync(filePath, newLines.join("\n"));
|
|
13697
13903
|
if (item.source === "personal") {
|
|
13698
|
-
|
|
13904
|
+
fs47.chmodSync(filePath, 384);
|
|
13699
13905
|
}
|
|
13700
13906
|
const roiStats = readROIStats();
|
|
13701
13907
|
const cleanText = bulletToRemove.replace(/^- /, "").replace(/^\*\*\[[^\]]+\]\*\*\s*/, "").trim();
|
|
@@ -13868,7 +14074,7 @@ async function insightsCommand(options) {
|
|
|
13868
14074
|
}
|
|
13869
14075
|
|
|
13870
14076
|
// src/commands/sources.ts
|
|
13871
|
-
import
|
|
14077
|
+
import fs48 from "fs";
|
|
13872
14078
|
import path39 from "path";
|
|
13873
14079
|
import chalk25 from "chalk";
|
|
13874
14080
|
init_resolve_caliber();
|
|
@@ -13886,9 +14092,9 @@ async function sourcesListCommand() {
|
|
|
13886
14092
|
if (configSources.length > 0) {
|
|
13887
14093
|
for (const source of configSources) {
|
|
13888
14094
|
const sourcePath = source.path || source.url || "";
|
|
13889
|
-
const exists = source.path ?
|
|
14095
|
+
const exists = source.path ? fs48.existsSync(path39.resolve(dir, source.path)) : false;
|
|
13890
14096
|
const status = exists ? chalk25.green("reachable") : chalk25.red("not found");
|
|
13891
|
-
const hasSummary = source.path &&
|
|
14097
|
+
const hasSummary = source.path && fs48.existsSync(path39.join(path39.resolve(dir, source.path), ".caliber", "summary.json"));
|
|
13892
14098
|
console.log(` ${chalk25.bold(source.role || source.type)} ${chalk25.dim(sourcePath)}`);
|
|
13893
14099
|
console.log(` Type: ${source.type} Status: ${status}${hasSummary ? " " + chalk25.cyan("has summary.json") : ""}`);
|
|
13894
14100
|
if (source.description) console.log(` ${chalk25.dim(source.description)}`);
|
|
@@ -13898,7 +14104,7 @@ async function sourcesListCommand() {
|
|
|
13898
14104
|
if (workspaces.length > 0) {
|
|
13899
14105
|
console.log(chalk25.dim(" Auto-detected workspaces:"));
|
|
13900
14106
|
for (const ws of workspaces) {
|
|
13901
|
-
const exists =
|
|
14107
|
+
const exists = fs48.existsSync(path39.resolve(dir, ws));
|
|
13902
14108
|
console.log(` ${exists ? chalk25.green("\u25CF") : chalk25.red("\u25CF")} ${ws}`);
|
|
13903
14109
|
}
|
|
13904
14110
|
console.log("");
|
|
@@ -13907,7 +14113,7 @@ async function sourcesListCommand() {
|
|
|
13907
14113
|
async function sourcesAddCommand(sourcePath) {
|
|
13908
14114
|
const dir = process.cwd();
|
|
13909
14115
|
const absPath = path39.resolve(dir, sourcePath);
|
|
13910
|
-
if (!
|
|
14116
|
+
if (!fs48.existsSync(absPath)) {
|
|
13911
14117
|
console.log(chalk25.red(`
|
|
13912
14118
|
Path not found: ${sourcePath}
|
|
13913
14119
|
`));
|
|
@@ -13970,7 +14176,7 @@ async function sourcesRemoveCommand(name) {
|
|
|
13970
14176
|
}
|
|
13971
14177
|
|
|
13972
14178
|
// src/commands/publish.ts
|
|
13973
|
-
import
|
|
14179
|
+
import fs49 from "fs";
|
|
13974
14180
|
import path40 from "path";
|
|
13975
14181
|
import chalk26 from "chalk";
|
|
13976
14182
|
import ora7 from "ora";
|
|
@@ -14015,11 +14221,11 @@ async function publishCommand() {
|
|
|
14015
14221
|
} catch {
|
|
14016
14222
|
}
|
|
14017
14223
|
const outputDir = path40.join(dir, ".caliber");
|
|
14018
|
-
if (!
|
|
14019
|
-
|
|
14224
|
+
if (!fs49.existsSync(outputDir)) {
|
|
14225
|
+
fs49.mkdirSync(outputDir, { recursive: true });
|
|
14020
14226
|
}
|
|
14021
14227
|
const outputPath = path40.join(outputDir, "summary.json");
|
|
14022
|
-
|
|
14228
|
+
fs49.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + "\n", "utf-8");
|
|
14023
14229
|
spinner.succeed("Project summary published");
|
|
14024
14230
|
console.log(` ${chalk26.green("\u2713")} ${path40.relative(dir, outputPath)}`);
|
|
14025
14231
|
console.log(chalk26.dim("\n Other projects can now reference this repo as a source."));
|
|
@@ -14034,7 +14240,7 @@ async function publishCommand() {
|
|
|
14034
14240
|
|
|
14035
14241
|
// src/commands/bootstrap.ts
|
|
14036
14242
|
init_builtin_skills();
|
|
14037
|
-
import
|
|
14243
|
+
import fs50 from "fs";
|
|
14038
14244
|
import chalk27 from "chalk";
|
|
14039
14245
|
var PLATFORM_SKILL_DIRS = {
|
|
14040
14246
|
claude: ".claude/skills",
|
|
@@ -14055,8 +14261,8 @@ async function bootstrapCommand() {
|
|
|
14055
14261
|
for (const skill of BUILTIN_SKILLS) {
|
|
14056
14262
|
const skillDir = `${skillsDir}/${skill.name}`;
|
|
14057
14263
|
const skillPath = `${skillDir}/SKILL.md`;
|
|
14058
|
-
|
|
14059
|
-
|
|
14264
|
+
fs50.mkdirSync(skillDir, { recursive: true });
|
|
14265
|
+
fs50.writeFileSync(skillPath, buildSkillContent(skill));
|
|
14060
14266
|
written.push(skillPath);
|
|
14061
14267
|
}
|
|
14062
14268
|
}
|
|
@@ -14073,7 +14279,7 @@ async function bootstrapCommand() {
|
|
|
14073
14279
|
}
|
|
14074
14280
|
|
|
14075
14281
|
// src/commands/uninstall.ts
|
|
14076
|
-
import
|
|
14282
|
+
import fs51 from "fs";
|
|
14077
14283
|
import path41 from "path";
|
|
14078
14284
|
import chalk28 from "chalk";
|
|
14079
14285
|
import confirm3 from "@inquirer/confirm";
|
|
@@ -14090,11 +14296,11 @@ var CURSOR_RULES_DIR = path41.join(".cursor", "rules");
|
|
|
14090
14296
|
var CLAUDE_RULES_DIR = path41.join(".claude", "rules");
|
|
14091
14297
|
function removeCaliberManagedFiles(dir, extension) {
|
|
14092
14298
|
const removed = [];
|
|
14093
|
-
if (!
|
|
14094
|
-
for (const file of
|
|
14299
|
+
if (!fs51.existsSync(dir)) return removed;
|
|
14300
|
+
for (const file of fs51.readdirSync(dir)) {
|
|
14095
14301
|
if (file.startsWith(CALIBER_MANAGED_PREFIX) && file.endsWith(extension)) {
|
|
14096
14302
|
const fullPath = path41.join(dir, file);
|
|
14097
|
-
|
|
14303
|
+
fs51.unlinkSync(fullPath);
|
|
14098
14304
|
removed.push(fullPath);
|
|
14099
14305
|
}
|
|
14100
14306
|
}
|
|
@@ -14103,11 +14309,11 @@ function removeCaliberManagedFiles(dir, extension) {
|
|
|
14103
14309
|
function removeBuiltinSkills() {
|
|
14104
14310
|
const removed = [];
|
|
14105
14311
|
for (const skillsDir of SKILL_DIRS) {
|
|
14106
|
-
if (!
|
|
14312
|
+
if (!fs51.existsSync(skillsDir)) continue;
|
|
14107
14313
|
for (const name of BUILTIN_SKILL_NAMES) {
|
|
14108
14314
|
const skillDir = path41.join(skillsDir, name);
|
|
14109
|
-
if (
|
|
14110
|
-
|
|
14315
|
+
if (fs51.existsSync(skillDir)) {
|
|
14316
|
+
fs51.rmSync(skillDir, { recursive: true });
|
|
14111
14317
|
removed.push(skillDir);
|
|
14112
14318
|
}
|
|
14113
14319
|
}
|
|
@@ -14117,15 +14323,15 @@ function removeBuiltinSkills() {
|
|
|
14117
14323
|
function stripManagedBlocksFromFiles() {
|
|
14118
14324
|
const modified = [];
|
|
14119
14325
|
for (const filePath of MANAGED_DOC_FILES) {
|
|
14120
|
-
if (!
|
|
14121
|
-
const original =
|
|
14326
|
+
if (!fs51.existsSync(filePath)) continue;
|
|
14327
|
+
const original = fs51.readFileSync(filePath, "utf-8");
|
|
14122
14328
|
const stripped = stripManagedBlocks(original);
|
|
14123
14329
|
if (stripped !== original) {
|
|
14124
14330
|
const trimmed = stripped.trim();
|
|
14125
14331
|
if (!trimmed || /^#\s*\S*$/.test(trimmed)) {
|
|
14126
|
-
|
|
14332
|
+
fs51.unlinkSync(filePath);
|
|
14127
14333
|
} else {
|
|
14128
|
-
|
|
14334
|
+
fs51.writeFileSync(filePath, stripped);
|
|
14129
14335
|
}
|
|
14130
14336
|
modified.push(filePath);
|
|
14131
14337
|
}
|
|
@@ -14133,8 +14339,8 @@ function stripManagedBlocksFromFiles() {
|
|
|
14133
14339
|
return modified;
|
|
14134
14340
|
}
|
|
14135
14341
|
function removeDirectory(dir) {
|
|
14136
|
-
if (!
|
|
14137
|
-
|
|
14342
|
+
if (!fs51.existsSync(dir)) return false;
|
|
14343
|
+
fs51.rmSync(dir, { recursive: true });
|
|
14138
14344
|
return true;
|
|
14139
14345
|
}
|
|
14140
14346
|
async function uninstallCommand(options) {
|
|
@@ -14206,8 +14412,8 @@ async function uninstallCommand(options) {
|
|
|
14206
14412
|
console.log(` ${chalk28.red("\u2717")} ${skill}/`);
|
|
14207
14413
|
}
|
|
14208
14414
|
if (removedSkills.length > 0) actions.push("builtin skills");
|
|
14209
|
-
if (
|
|
14210
|
-
|
|
14415
|
+
if (fs51.existsSync("CALIBER_LEARNINGS.md")) {
|
|
14416
|
+
fs51.unlinkSync("CALIBER_LEARNINGS.md");
|
|
14211
14417
|
console.log(` ${chalk28.red("\u2717")} CALIBER_LEARNINGS.md`);
|
|
14212
14418
|
actions.push("learnings file");
|
|
14213
14419
|
}
|
|
@@ -14221,18 +14427,18 @@ async function uninstallCommand(options) {
|
|
|
14221
14427
|
}
|
|
14222
14428
|
trackUninstallExecuted();
|
|
14223
14429
|
const configPath = getConfigFilePath();
|
|
14224
|
-
if (
|
|
14430
|
+
if (fs51.existsSync(configPath)) {
|
|
14225
14431
|
console.log("");
|
|
14226
14432
|
const removeConfig = options.force || await confirm3({
|
|
14227
14433
|
message: `Remove global config (~/.caliber/config.json)? This affects all projects.`
|
|
14228
14434
|
});
|
|
14229
14435
|
if (removeConfig) {
|
|
14230
|
-
|
|
14436
|
+
fs51.unlinkSync(configPath);
|
|
14231
14437
|
console.log(` ${chalk28.red("\u2717")} ${configPath}`);
|
|
14232
14438
|
const configDir = path41.dirname(configPath);
|
|
14233
14439
|
try {
|
|
14234
|
-
const remaining =
|
|
14235
|
-
if (remaining.length === 0)
|
|
14440
|
+
const remaining = fs51.readdirSync(configDir);
|
|
14441
|
+
if (remaining.length === 0) fs51.rmdirSync(configDir);
|
|
14236
14442
|
} catch {
|
|
14237
14443
|
}
|
|
14238
14444
|
}
|
|
@@ -14244,7 +14450,7 @@ async function uninstallCommand(options) {
|
|
|
14244
14450
|
|
|
14245
14451
|
// src/cli.ts
|
|
14246
14452
|
var __dirname = path42.dirname(fileURLToPath(import.meta.url));
|
|
14247
|
-
var pkg = JSON.parse(
|
|
14453
|
+
var pkg = JSON.parse(fs52.readFileSync(path42.resolve(__dirname, "..", "package.json"), "utf-8"));
|
|
14248
14454
|
var program = new Command();
|
|
14249
14455
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
14250
14456
|
program.name(process.env.CALIBER_LOCAL ? "caloc" : "caliber").description("AI context infrastructure for coding agents").version(displayVersion).option("--no-traces", "Disable anonymous telemetry for this run");
|
|
@@ -14355,15 +14561,15 @@ learn.command("delete <index>").description("Delete a learning by its index numb
|
|
|
14355
14561
|
learn.command("add <content>").description("Add a learning directly (used by agent skills)").option("--personal", "Save as a personal learning instead of project-level").action(tracked("learn:add", learnAddCommand));
|
|
14356
14562
|
|
|
14357
14563
|
// src/utils/version-check.ts
|
|
14358
|
-
import
|
|
14564
|
+
import fs53 from "fs";
|
|
14359
14565
|
import path43 from "path";
|
|
14360
14566
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14361
|
-
import { execSync as execSync16, execFileSync as
|
|
14567
|
+
import { execSync as execSync16, execFileSync as execFileSync5 } from "child_process";
|
|
14362
14568
|
import chalk29 from "chalk";
|
|
14363
14569
|
import ora8 from "ora";
|
|
14364
14570
|
import confirm4 from "@inquirer/confirm";
|
|
14365
14571
|
var __dirname_vc = path43.dirname(fileURLToPath2(import.meta.url));
|
|
14366
|
-
var pkg2 = JSON.parse(
|
|
14572
|
+
var pkg2 = JSON.parse(fs53.readFileSync(path43.resolve(__dirname_vc, "..", "package.json"), "utf-8"));
|
|
14367
14573
|
function getChannel(version) {
|
|
14368
14574
|
const match = version.match(/-(dev|next)\./);
|
|
14369
14575
|
return match ? match[1] : "latest";
|
|
@@ -14391,7 +14597,7 @@ function getInstalledVersion() {
|
|
|
14391
14597
|
stdio: ["pipe", "pipe", "pipe"]
|
|
14392
14598
|
}).trim();
|
|
14393
14599
|
const pkgPath = path43.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
14394
|
-
return JSON.parse(
|
|
14600
|
+
return JSON.parse(fs53.readFileSync(pkgPath, "utf-8")).version;
|
|
14395
14601
|
} catch {
|
|
14396
14602
|
return null;
|
|
14397
14603
|
}
|
|
@@ -14439,7 +14645,7 @@ Update available: ${current} -> ${latest}`));
|
|
|
14439
14645
|
if (!/^[\w.-]+$/.test(tag)) return;
|
|
14440
14646
|
const spinner = ora8("Updating caliber...").start();
|
|
14441
14647
|
try {
|
|
14442
|
-
|
|
14648
|
+
execFileSync5("npm", ["install", "-g", `@rely-ai/caliber@${tag}`], {
|
|
14443
14649
|
stdio: "pipe",
|
|
14444
14650
|
timeout: 12e4,
|
|
14445
14651
|
env: { ...process.env, npm_config_fund: "false", npm_config_audit: "false" }
|
|
@@ -14458,7 +14664,7 @@ Update available: ${current} -> ${latest}`));
|
|
|
14458
14664
|
console.log(chalk29.dim(`
|
|
14459
14665
|
Restarting: caliber ${args.join(" ")}
|
|
14460
14666
|
`));
|
|
14461
|
-
|
|
14667
|
+
execFileSync5("caliber", args, {
|
|
14462
14668
|
stdio: "inherit",
|
|
14463
14669
|
env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
|
|
14464
14670
|
});
|