@rely-ai/caliber 1.41.4 → 1.43.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 +775 -479
- 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;
|
|
@@ -234,6 +262,7 @@ var pre_commit_block_exports = {};
|
|
|
234
262
|
__export(pre_commit_block_exports, {
|
|
235
263
|
appendLearningsBlock: () => appendLearningsBlock,
|
|
236
264
|
appendManagedBlocks: () => appendManagedBlocks,
|
|
265
|
+
appendModelBlock: () => appendModelBlock,
|
|
237
266
|
appendPreCommitBlock: () => appendPreCommitBlock,
|
|
238
267
|
appendSyncBlock: () => appendSyncBlock,
|
|
239
268
|
getCursorLearningsRule: () => getCursorLearningsRule,
|
|
@@ -241,6 +270,7 @@ __export(pre_commit_block_exports, {
|
|
|
241
270
|
getCursorSetupRule: () => getCursorSetupRule,
|
|
242
271
|
getCursorSyncRule: () => getCursorSyncRule,
|
|
243
272
|
hasLearningsBlock: () => hasLearningsBlock,
|
|
273
|
+
hasModelBlock: () => hasModelBlock,
|
|
244
274
|
hasPreCommitBlock: () => hasPreCommitBlock,
|
|
245
275
|
hasSyncBlock: () => hasSyncBlock,
|
|
246
276
|
stripManagedBlocks: () => stripManagedBlocks
|
|
@@ -317,6 +347,25 @@ function appendLearningsBlock(content) {
|
|
|
317
347
|
function getCursorLearningsRule() {
|
|
318
348
|
return { filename: CURSOR_LEARNINGS_FILENAME, content: CURSOR_LEARNINGS_CONTENT };
|
|
319
349
|
}
|
|
350
|
+
function buildManagedModelBlock() {
|
|
351
|
+
const m = DEFAULT_MODELS.anthropic;
|
|
352
|
+
return `${MODEL_BLOCK_START}
|
|
353
|
+
## Model Configuration
|
|
354
|
+
|
|
355
|
+
Recommended default: \`${m}\` with high effort (stronger reasoning; higher cost and latency than smaller models).
|
|
356
|
+
Smaller/faster models trade quality for speed and cost \u2014 pick what fits the task.
|
|
357
|
+
Pin your choice (\`/model\` in Claude Code, or \`CALIBER_MODEL\` when using Caliber with an API provider) so upstream default changes do not silently change behavior.
|
|
358
|
+
|
|
359
|
+
${MODEL_BLOCK_END}`;
|
|
360
|
+
}
|
|
361
|
+
function hasModelBlock(content) {
|
|
362
|
+
return content.includes(MODEL_BLOCK_START);
|
|
363
|
+
}
|
|
364
|
+
function appendModelBlock(content) {
|
|
365
|
+
if (hasModelBlock(content)) return content;
|
|
366
|
+
const trimmed = content.trimEnd();
|
|
367
|
+
return trimmed + "\n\n" + buildManagedModelBlock() + "\n";
|
|
368
|
+
}
|
|
320
369
|
function getSyncSetupInstruction(platform) {
|
|
321
370
|
switch (platform) {
|
|
322
371
|
case "claude":
|
|
@@ -351,7 +400,10 @@ function appendSyncBlock(content, platform = "claude") {
|
|
|
351
400
|
return trimmed + "\n\n" + getSyncBlock(platform) + "\n";
|
|
352
401
|
}
|
|
353
402
|
function appendManagedBlocks(content, platform = "claude") {
|
|
354
|
-
return appendSyncBlock(
|
|
403
|
+
return appendSyncBlock(
|
|
404
|
+
appendModelBlock(appendLearningsBlock(appendPreCommitBlock(content, platform))),
|
|
405
|
+
platform
|
|
406
|
+
);
|
|
355
407
|
}
|
|
356
408
|
function getCursorSyncContent() {
|
|
357
409
|
const bin = resolveCaliber();
|
|
@@ -379,7 +431,7 @@ grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "SYNCED" || echo "NO
|
|
|
379
431
|
\`\`\`
|
|
380
432
|
|
|
381
433
|
If SYNCED: proceed normally.
|
|
382
|
-
If NOT_SYNCED: Tell the user: "This project uses Caliber to keep agent configs in sync
|
|
434
|
+
If NOT_SYNCED: Tell the user: "This project uses Caliber to keep agent configs in sync \u2014 it is not set up yet. Want me to run /setup-caliber? (~30 seconds)". If they agree, read \`.cursor/skills/setup-caliber/SKILL.md\` and follow its instructions.
|
|
383
435
|
`;
|
|
384
436
|
}
|
|
385
437
|
function getCursorSetupRule() {
|
|
@@ -396,11 +448,12 @@ function stripManagedBlocks(content) {
|
|
|
396
448
|
}
|
|
397
449
|
return result.replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
398
450
|
}
|
|
399
|
-
var BLOCK_START, BLOCK_END, MANAGED_DOC_PATHS, CURSOR_RULE_FILENAME, LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END, LEARNINGS_BLOCK, CURSOR_LEARNINGS_FILENAME, CURSOR_LEARNINGS_CONTENT, SYNC_BLOCK_START, SYNC_BLOCK_END, CURSOR_SYNC_FILENAME, CURSOR_SETUP_FILENAME, MANAGED_BLOCK_PAIRS;
|
|
451
|
+
var BLOCK_START, BLOCK_END, MANAGED_DOC_PATHS, CURSOR_RULE_FILENAME, LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END, LEARNINGS_BLOCK, CURSOR_LEARNINGS_FILENAME, CURSOR_LEARNINGS_CONTENT, MODEL_BLOCK_START, MODEL_BLOCK_END, SYNC_BLOCK_START, SYNC_BLOCK_END, CURSOR_SYNC_FILENAME, CURSOR_SETUP_FILENAME, MANAGED_BLOCK_PAIRS;
|
|
400
452
|
var init_pre_commit_block = __esm({
|
|
401
453
|
"src/writers/pre-commit-block.ts"() {
|
|
402
454
|
"use strict";
|
|
403
455
|
init_resolve_caliber();
|
|
456
|
+
init_config();
|
|
404
457
|
BLOCK_START = "<!-- caliber:managed:pre-commit -->";
|
|
405
458
|
BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
|
|
406
459
|
MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md .agents/ .opencode/";
|
|
@@ -421,6 +474,8 @@ alwaysApply: true
|
|
|
421
474
|
Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
|
|
422
475
|
These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
|
|
423
476
|
`;
|
|
477
|
+
MODEL_BLOCK_START = "<!-- caliber:managed:model-config -->";
|
|
478
|
+
MODEL_BLOCK_END = "<!-- /caliber:managed:model-config -->";
|
|
424
479
|
SYNC_BLOCK_START = "<!-- caliber:managed:sync -->";
|
|
425
480
|
SYNC_BLOCK_END = "<!-- /caliber:managed:sync -->";
|
|
426
481
|
CURSOR_SYNC_FILENAME = "caliber-sync.mdc";
|
|
@@ -428,6 +483,7 @@ These are auto-extracted from real tool usage \u2014 treat them as project-speci
|
|
|
428
483
|
MANAGED_BLOCK_PAIRS = [
|
|
429
484
|
[BLOCK_START, BLOCK_END],
|
|
430
485
|
[LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END],
|
|
486
|
+
[MODEL_BLOCK_START, MODEL_BLOCK_END],
|
|
431
487
|
[SYNC_BLOCK_START, SYNC_BLOCK_END]
|
|
432
488
|
];
|
|
433
489
|
}
|
|
@@ -445,7 +501,7 @@ __export(builtin_skills_exports, {
|
|
|
445
501
|
buildSkillContent: () => buildSkillContent,
|
|
446
502
|
ensureBuiltinSkills: () => ensureBuiltinSkills
|
|
447
503
|
});
|
|
448
|
-
import
|
|
504
|
+
import fs17 from "fs";
|
|
449
505
|
import path16 from "path";
|
|
450
506
|
function buildSkillContent(skill) {
|
|
451
507
|
const frontmatter = `---
|
|
@@ -779,11 +835,11 @@ From now on, every commit keeps all your agent configs in sync automatically.
|
|
|
779
835
|
function ensureBuiltinSkills() {
|
|
780
836
|
const written = [];
|
|
781
837
|
for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
|
|
782
|
-
if (!
|
|
838
|
+
if (!fs17.existsSync(platformDir)) continue;
|
|
783
839
|
for (const skill of BUILTIN_SKILLS) {
|
|
784
840
|
const skillPath = path16.join(skillsDir, skill.name, "SKILL.md");
|
|
785
|
-
|
|
786
|
-
|
|
841
|
+
fs17.mkdirSync(path16.dirname(skillPath), { recursive: true });
|
|
842
|
+
fs17.writeFileSync(skillPath, buildSkillContent(skill));
|
|
787
843
|
written.push(skillPath);
|
|
788
844
|
}
|
|
789
845
|
}
|
|
@@ -828,13 +884,13 @@ var init_builtin_skills = __esm({
|
|
|
828
884
|
|
|
829
885
|
// src/utils/editor.ts
|
|
830
886
|
import { execSync as execSync14, spawn as spawn3 } from "child_process";
|
|
831
|
-
import
|
|
887
|
+
import fs29 from "fs";
|
|
832
888
|
import path25 from "path";
|
|
833
889
|
import os6 from "os";
|
|
834
890
|
function getEmptyFilePath(proposedPath) {
|
|
835
|
-
|
|
891
|
+
fs29.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
|
|
836
892
|
const tempPath = path25.join(DIFF_TEMP_DIR, path25.basename(proposedPath));
|
|
837
|
-
|
|
893
|
+
fs29.writeFileSync(tempPath, "");
|
|
838
894
|
return tempPath;
|
|
839
895
|
}
|
|
840
896
|
function commandExists(cmd) {
|
|
@@ -886,10 +942,11 @@ __export(review_exports, {
|
|
|
886
942
|
promptWantsReview: () => promptWantsReview
|
|
887
943
|
});
|
|
888
944
|
import chalk10 from "chalk";
|
|
889
|
-
import
|
|
945
|
+
import fs30 from "fs";
|
|
890
946
|
import select4 from "@inquirer/select";
|
|
891
947
|
import { createTwoFilesPatch } from "diff";
|
|
892
948
|
async function promptWantsReview() {
|
|
949
|
+
if (!process.stdin.isTTY) return false;
|
|
893
950
|
return select4({
|
|
894
951
|
message: "Would you like to review the diffs before deciding?",
|
|
895
952
|
choices: [
|
|
@@ -901,6 +958,7 @@ async function promptWantsReview() {
|
|
|
901
958
|
async function promptReviewMethod() {
|
|
902
959
|
const available = detectAvailableEditors();
|
|
903
960
|
if (available.length === 1) return "terminal";
|
|
961
|
+
if (!process.stdin.isTTY) return "terminal";
|
|
904
962
|
const choices = available.map((method) => {
|
|
905
963
|
switch (method) {
|
|
906
964
|
case "cursor":
|
|
@@ -915,16 +973,19 @@ async function promptReviewMethod() {
|
|
|
915
973
|
}
|
|
916
974
|
async function openReview(method, stagedFiles) {
|
|
917
975
|
if (method === "cursor" || method === "vscode") {
|
|
918
|
-
openDiffsInEditor(
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
976
|
+
openDiffsInEditor(
|
|
977
|
+
method,
|
|
978
|
+
stagedFiles.map((f) => ({
|
|
979
|
+
originalPath: f.originalPath,
|
|
980
|
+
proposedPath: f.proposedPath
|
|
981
|
+
}))
|
|
982
|
+
);
|
|
922
983
|
console.log(chalk10.dim(" Diffs opened in your editor.\n"));
|
|
923
984
|
return;
|
|
924
985
|
}
|
|
925
986
|
const fileInfos = stagedFiles.map((file) => {
|
|
926
|
-
const proposed =
|
|
927
|
-
const current = file.currentPath ?
|
|
987
|
+
const proposed = fs30.readFileSync(file.proposedPath, "utf-8");
|
|
988
|
+
const current = file.currentPath ? fs30.readFileSync(file.currentPath, "utf-8") : "";
|
|
928
989
|
const patch = createTwoFilesPatch(
|
|
929
990
|
file.isNew ? "/dev/null" : file.relativePath,
|
|
930
991
|
file.relativePath,
|
|
@@ -1099,7 +1160,7 @@ __export(lock_exports, {
|
|
|
1099
1160
|
isCaliberRunning: () => isCaliberRunning,
|
|
1100
1161
|
releaseLock: () => releaseLock
|
|
1101
1162
|
});
|
|
1102
|
-
import
|
|
1163
|
+
import fs41 from "fs";
|
|
1103
1164
|
import path33 from "path";
|
|
1104
1165
|
import os8 from "os";
|
|
1105
1166
|
import crypto5 from "crypto";
|
|
@@ -1115,8 +1176,8 @@ function getLockFile() {
|
|
|
1115
1176
|
function isCaliberRunning() {
|
|
1116
1177
|
try {
|
|
1117
1178
|
const lockFile = buildLockPath();
|
|
1118
|
-
if (!
|
|
1119
|
-
const raw =
|
|
1179
|
+
if (!fs41.existsSync(lockFile)) return false;
|
|
1180
|
+
const raw = fs41.readFileSync(lockFile, "utf-8").trim();
|
|
1120
1181
|
const { pid, ts } = JSON.parse(raw);
|
|
1121
1182
|
if (pid === process.pid) return false;
|
|
1122
1183
|
if (Date.now() - ts > STALE_MS) return false;
|
|
@@ -1132,14 +1193,14 @@ function isCaliberRunning() {
|
|
|
1132
1193
|
}
|
|
1133
1194
|
function acquireLock() {
|
|
1134
1195
|
try {
|
|
1135
|
-
|
|
1196
|
+
fs41.writeFileSync(getLockFile(), JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
1136
1197
|
} catch {
|
|
1137
1198
|
}
|
|
1138
1199
|
}
|
|
1139
1200
|
function releaseLock() {
|
|
1140
1201
|
try {
|
|
1141
1202
|
const lockFile = getLockFile();
|
|
1142
|
-
if (
|
|
1203
|
+
if (fs41.existsSync(lockFile)) fs41.unlinkSync(lockFile);
|
|
1143
1204
|
} catch {
|
|
1144
1205
|
}
|
|
1145
1206
|
}
|
|
@@ -1154,17 +1215,17 @@ var init_lock = __esm({
|
|
|
1154
1215
|
|
|
1155
1216
|
// src/cli.ts
|
|
1156
1217
|
import { Command } from "commander";
|
|
1157
|
-
import
|
|
1218
|
+
import fs52 from "fs";
|
|
1158
1219
|
import path42 from "path";
|
|
1159
1220
|
import { fileURLToPath } from "url";
|
|
1160
1221
|
|
|
1161
1222
|
// src/commands/init.ts
|
|
1162
1223
|
import path28 from "path";
|
|
1163
1224
|
import chalk14 from "chalk";
|
|
1164
|
-
import
|
|
1225
|
+
import fs35 from "fs";
|
|
1165
1226
|
|
|
1166
1227
|
// src/fingerprint/index.ts
|
|
1167
|
-
import
|
|
1228
|
+
import fs9 from "fs";
|
|
1168
1229
|
import path8 from "path";
|
|
1169
1230
|
|
|
1170
1231
|
// src/fingerprint/git.ts
|
|
@@ -2358,7 +2419,7 @@ var OpenAICompatProvider = class {
|
|
|
2358
2419
|
};
|
|
2359
2420
|
|
|
2360
2421
|
// src/llm/cursor-acp.ts
|
|
2361
|
-
import { spawn, execSync as execSync5 } from "child_process";
|
|
2422
|
+
import { spawn, execSync as execSync5, execFileSync } from "child_process";
|
|
2362
2423
|
import os3 from "os";
|
|
2363
2424
|
|
|
2364
2425
|
// src/llm/seat-based-errors.ts
|
|
@@ -2430,8 +2491,23 @@ function estimateTokens(text) {
|
|
|
2430
2491
|
}
|
|
2431
2492
|
|
|
2432
2493
|
// src/llm/cursor-acp.ts
|
|
2433
|
-
var AGENT_BIN = "agent";
|
|
2434
2494
|
var IS_WINDOWS = process.platform === "win32";
|
|
2495
|
+
var _agentBin = null;
|
|
2496
|
+
function resolveAgentBin() {
|
|
2497
|
+
if (_agentBin !== null) return _agentBin;
|
|
2498
|
+
try {
|
|
2499
|
+
const whichCmd = IS_WINDOWS ? "where agent" : "which agent";
|
|
2500
|
+
const out = execSync5(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
2501
|
+
const p = out.split("\n")[0].trim();
|
|
2502
|
+
if (p) {
|
|
2503
|
+
_agentBin = p;
|
|
2504
|
+
return _agentBin;
|
|
2505
|
+
}
|
|
2506
|
+
} catch {
|
|
2507
|
+
}
|
|
2508
|
+
_agentBin = "agent";
|
|
2509
|
+
return _agentBin;
|
|
2510
|
+
}
|
|
2435
2511
|
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
2436
2512
|
var SIGKILL_DELAY_MS = 5e3;
|
|
2437
2513
|
var STDERR_MAX_BYTES = 10 * 1024;
|
|
@@ -2489,7 +2565,7 @@ var CursorAcpProvider = class {
|
|
|
2489
2565
|
const targetModel = model || this.defaultModel;
|
|
2490
2566
|
if (this.warmProcess && !this.warmProcess.killed && this.warmModel === targetModel) return;
|
|
2491
2567
|
const args = this.buildArgs(targetModel, false);
|
|
2492
|
-
this.warmProcess = spawn(
|
|
2568
|
+
this.warmProcess = spawn(resolveAgentBin(), args, {
|
|
2493
2569
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2494
2570
|
env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
|
|
2495
2571
|
...IS_WINDOWS && { shell: true }
|
|
@@ -2536,7 +2612,7 @@ var CursorAcpProvider = class {
|
|
|
2536
2612
|
return { child: warm, stderrChunks: stderrChunks2 };
|
|
2537
2613
|
}
|
|
2538
2614
|
const args = this.buildArgs(model, streaming);
|
|
2539
|
-
const child = spawn(
|
|
2615
|
+
const child = spawn(resolveAgentBin(), args, {
|
|
2540
2616
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2541
2617
|
env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
|
|
2542
2618
|
...IS_WINDOWS && { shell: true }
|
|
@@ -2710,9 +2786,9 @@ var CursorAcpProvider = class {
|
|
|
2710
2786
|
}
|
|
2711
2787
|
};
|
|
2712
2788
|
function isCursorAgentAvailable() {
|
|
2789
|
+
if (resolveAgentBin() !== "agent") return true;
|
|
2713
2790
|
try {
|
|
2714
|
-
|
|
2715
|
-
execSync5(cmd, { stdio: "ignore" });
|
|
2791
|
+
execSync5(IS_WINDOWS ? "where agent" : "which agent", { stdio: "ignore" });
|
|
2716
2792
|
return true;
|
|
2717
2793
|
} catch {
|
|
2718
2794
|
return false;
|
|
@@ -2720,7 +2796,7 @@ function isCursorAgentAvailable() {
|
|
|
2720
2796
|
}
|
|
2721
2797
|
function isCursorLoggedIn() {
|
|
2722
2798
|
try {
|
|
2723
|
-
const result =
|
|
2799
|
+
const result = execFileSync(resolveAgentBin(), ["status"], {
|
|
2724
2800
|
input: "",
|
|
2725
2801
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2726
2802
|
timeout: 5e3
|
|
@@ -2732,18 +2808,56 @@ function isCursorLoggedIn() {
|
|
|
2732
2808
|
}
|
|
2733
2809
|
|
|
2734
2810
|
// src/llm/claude-cli.ts
|
|
2735
|
-
import
|
|
2736
|
-
|
|
2811
|
+
import fs7 from "fs";
|
|
2812
|
+
import { spawn as spawn2, execSync as execSync6, execFileSync as execFileSync2 } from "child_process";
|
|
2737
2813
|
var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
2738
2814
|
var IS_WINDOWS2 = process.platform === "win32";
|
|
2815
|
+
function candidateClaudePaths() {
|
|
2816
|
+
if (IS_WINDOWS2) return [];
|
|
2817
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
2818
|
+
return [
|
|
2819
|
+
`${home}/.local/bin/claude`,
|
|
2820
|
+
// Claude Code default installer path
|
|
2821
|
+
"/usr/local/bin/claude",
|
|
2822
|
+
// Homebrew / manual install
|
|
2823
|
+
"/opt/homebrew/bin/claude"
|
|
2824
|
+
// Apple Silicon Homebrew
|
|
2825
|
+
].filter(Boolean);
|
|
2826
|
+
}
|
|
2827
|
+
var _claudeBin = null;
|
|
2828
|
+
function resolveClaudeBin() {
|
|
2829
|
+
if (_claudeBin !== null) return _claudeBin;
|
|
2830
|
+
try {
|
|
2831
|
+
const whichCmd = IS_WINDOWS2 ? "where claude" : "which claude";
|
|
2832
|
+
const out = execSync6(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
2833
|
+
const p = out.split("\n")[0].trim();
|
|
2834
|
+
if (p) {
|
|
2835
|
+
_claudeBin = p;
|
|
2836
|
+
return _claudeBin;
|
|
2837
|
+
}
|
|
2838
|
+
} catch {
|
|
2839
|
+
}
|
|
2840
|
+
for (const candidate of candidateClaudePaths()) {
|
|
2841
|
+
try {
|
|
2842
|
+
fs7.accessSync(candidate, fs7.constants.X_OK);
|
|
2843
|
+
_claudeBin = candidate;
|
|
2844
|
+
return _claudeBin;
|
|
2845
|
+
} catch {
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
_claudeBin = "claude";
|
|
2849
|
+
return _claudeBin;
|
|
2850
|
+
}
|
|
2739
2851
|
function spawnClaude(args) {
|
|
2740
|
-
const
|
|
2741
|
-
|
|
2852
|
+
const bin = resolveClaudeBin();
|
|
2853
|
+
const { CLAUDE_CODE_SIMPLE: _stripped, ...parentEnv } = process.env;
|
|
2854
|
+
const env = parentEnv;
|
|
2855
|
+
return IS_WINDOWS2 ? spawn2([bin, ...args].join(" "), {
|
|
2742
2856
|
cwd: process.cwd(),
|
|
2743
2857
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2744
2858
|
env,
|
|
2745
2859
|
shell: true
|
|
2746
|
-
}) : spawn2(
|
|
2860
|
+
}) : spawn2(bin, args, {
|
|
2747
2861
|
cwd: process.cwd(),
|
|
2748
2862
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2749
2863
|
env
|
|
@@ -2818,8 +2932,8 @@ var ClaudeCliProvider = class {
|
|
|
2818
2932
|
callbacks.onEnd({ stopReason: "end_turn" });
|
|
2819
2933
|
} else {
|
|
2820
2934
|
const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
|
|
2821
|
-
const friendly = parseSeatBasedError(stderr, code);
|
|
2822
2935
|
const stdout = Buffer.concat(chunks).toString("utf-8").trim();
|
|
2936
|
+
const friendly = parseSeatBasedError(stderr || stdout, code);
|
|
2823
2937
|
const base = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
|
|
2824
2938
|
const detail = friendly || stderr || (stdout ? stdout.slice(0, 200) : "");
|
|
2825
2939
|
callbacks.onError(new Error(detail ? `${base}. ${detail}` : base));
|
|
@@ -2879,7 +2993,7 @@ var ClaudeCliProvider = class {
|
|
|
2879
2993
|
resolve3(stdout);
|
|
2880
2994
|
} else {
|
|
2881
2995
|
const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
|
|
2882
|
-
const friendly = parseSeatBasedError(stderr, code);
|
|
2996
|
+
const friendly = parseSeatBasedError(stderr || stdout, code);
|
|
2883
2997
|
const base = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
|
|
2884
2998
|
const detail = friendly || stderr || (stdout ? stdout.slice(0, 200) : "");
|
|
2885
2999
|
reject(new Error(detail ? `${base}. ${detail}` : base));
|
|
@@ -2889,9 +3003,9 @@ var ClaudeCliProvider = class {
|
|
|
2889
3003
|
}
|
|
2890
3004
|
};
|
|
2891
3005
|
function isClaudeCliAvailable() {
|
|
3006
|
+
if (resolveClaudeBin() !== "claude") return true;
|
|
2892
3007
|
try {
|
|
2893
|
-
|
|
2894
|
-
execSync6(cmd, { stdio: "ignore" });
|
|
3008
|
+
execSync6(IS_WINDOWS2 ? "where claude" : "which claude", { stdio: "ignore" });
|
|
2895
3009
|
return true;
|
|
2896
3010
|
} catch {
|
|
2897
3011
|
return false;
|
|
@@ -2901,7 +3015,7 @@ var cachedLoggedIn = null;
|
|
|
2901
3015
|
function isClaudeCliLoggedIn() {
|
|
2902
3016
|
if (cachedLoggedIn !== null) return cachedLoggedIn;
|
|
2903
3017
|
try {
|
|
2904
|
-
const result =
|
|
3018
|
+
const result = execFileSync2(resolveClaudeBin(), ["auth", "status"], {
|
|
2905
3019
|
input: "",
|
|
2906
3020
|
stdio: ["pipe", "pipe", "pipe"],
|
|
2907
3021
|
timeout: 5e3
|
|
@@ -3720,7 +3834,7 @@ async function detectProjectStack(fileTree, suffixCounts) {
|
|
|
3720
3834
|
init_config();
|
|
3721
3835
|
|
|
3722
3836
|
// src/fingerprint/cache.ts
|
|
3723
|
-
import
|
|
3837
|
+
import fs8 from "fs";
|
|
3724
3838
|
import path7 from "path";
|
|
3725
3839
|
import crypto from "crypto";
|
|
3726
3840
|
import { execSync as execSync7 } from "child_process";
|
|
@@ -3765,8 +3879,8 @@ function computeTreeSignature(fileTree, dir) {
|
|
|
3765
3879
|
function loadFingerprintCache(dir, fileTree) {
|
|
3766
3880
|
const cachePath = getCachePath(dir);
|
|
3767
3881
|
try {
|
|
3768
|
-
if (!
|
|
3769
|
-
const raw =
|
|
3882
|
+
if (!fs8.existsSync(cachePath)) return null;
|
|
3883
|
+
const raw = fs8.readFileSync(cachePath, "utf-8");
|
|
3770
3884
|
const cache = JSON.parse(raw);
|
|
3771
3885
|
if (cache.version !== CACHE_VERSION) return null;
|
|
3772
3886
|
const currentHead = getGitHead(dir);
|
|
@@ -3788,8 +3902,8 @@ function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks
|
|
|
3788
3902
|
const cachePath = getCachePath(dir);
|
|
3789
3903
|
try {
|
|
3790
3904
|
const cacheDir = path7.dirname(cachePath);
|
|
3791
|
-
if (!
|
|
3792
|
-
|
|
3905
|
+
if (!fs8.existsSync(cacheDir)) {
|
|
3906
|
+
fs8.mkdirSync(cacheDir, { recursive: true });
|
|
3793
3907
|
}
|
|
3794
3908
|
const cache = {
|
|
3795
3909
|
version: CACHE_VERSION,
|
|
@@ -3801,15 +3915,15 @@ function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks
|
|
|
3801
3915
|
tools,
|
|
3802
3916
|
workspaces
|
|
3803
3917
|
};
|
|
3804
|
-
|
|
3918
|
+
fs8.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
|
|
3805
3919
|
} catch {
|
|
3806
3920
|
}
|
|
3807
3921
|
}
|
|
3808
3922
|
function getDetectedWorkspaces(dir) {
|
|
3809
3923
|
const cachePath = getCachePath(dir);
|
|
3810
3924
|
try {
|
|
3811
|
-
if (!
|
|
3812
|
-
const raw =
|
|
3925
|
+
if (!fs8.existsSync(cachePath)) return [];
|
|
3926
|
+
const raw = fs8.readFileSync(cachePath, "utf-8");
|
|
3813
3927
|
const cache = JSON.parse(raw);
|
|
3814
3928
|
return cache.workspaces ?? [];
|
|
3815
3929
|
} catch {
|
|
@@ -3862,8 +3976,8 @@ async function collectFingerprint(dir) {
|
|
|
3862
3976
|
function readPackageName(dir) {
|
|
3863
3977
|
try {
|
|
3864
3978
|
const pkgPath = path8.join(dir, "package.json");
|
|
3865
|
-
if (!
|
|
3866
|
-
const pkg3 = JSON.parse(
|
|
3979
|
+
if (!fs9.existsSync(pkgPath)) return void 0;
|
|
3980
|
+
const pkg3 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
3867
3981
|
return pkg3.name;
|
|
3868
3982
|
} catch {
|
|
3869
3983
|
return void 0;
|
|
@@ -3893,23 +4007,23 @@ async function enrichWithLLM(fingerprint) {
|
|
|
3893
4007
|
}
|
|
3894
4008
|
|
|
3895
4009
|
// src/scanner/index.ts
|
|
3896
|
-
import
|
|
4010
|
+
import fs10 from "fs";
|
|
3897
4011
|
import path9 from "path";
|
|
3898
4012
|
import crypto2 from "crypto";
|
|
3899
4013
|
import os4 from "os";
|
|
3900
4014
|
function detectPlatforms() {
|
|
3901
4015
|
const home = os4.homedir();
|
|
3902
4016
|
return {
|
|
3903
|
-
claude:
|
|
3904
|
-
cursor:
|
|
3905
|
-
codex:
|
|
3906
|
-
opencode:
|
|
4017
|
+
claude: fs10.existsSync(path9.join(home, ".claude")),
|
|
4018
|
+
cursor: fs10.existsSync(getCursorConfigDir()),
|
|
4019
|
+
codex: fs10.existsSync(path9.join(home, ".codex")),
|
|
4020
|
+
opencode: fs10.existsSync(path9.join(home, ".config", "opencode"))
|
|
3907
4021
|
};
|
|
3908
4022
|
}
|
|
3909
4023
|
function scanLocalState(dir) {
|
|
3910
4024
|
const items = [];
|
|
3911
4025
|
const claudeMdPath = path9.join(dir, "CLAUDE.md");
|
|
3912
|
-
if (
|
|
4026
|
+
if (fs10.existsSync(claudeMdPath)) {
|
|
3913
4027
|
items.push({
|
|
3914
4028
|
type: "rule",
|
|
3915
4029
|
platform: "claude",
|
|
@@ -3919,8 +4033,8 @@ function scanLocalState(dir) {
|
|
|
3919
4033
|
});
|
|
3920
4034
|
}
|
|
3921
4035
|
const skillsDir = path9.join(dir, ".claude", "skills");
|
|
3922
|
-
if (
|
|
3923
|
-
for (const file of
|
|
4036
|
+
if (fs10.existsSync(skillsDir)) {
|
|
4037
|
+
for (const file of fs10.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
3924
4038
|
const filePath = path9.join(skillsDir, file);
|
|
3925
4039
|
items.push({
|
|
3926
4040
|
type: "skill",
|
|
@@ -3932,9 +4046,9 @@ function scanLocalState(dir) {
|
|
|
3932
4046
|
}
|
|
3933
4047
|
}
|
|
3934
4048
|
const mcpJsonPath = path9.join(dir, ".mcp.json");
|
|
3935
|
-
if (
|
|
4049
|
+
if (fs10.existsSync(mcpJsonPath)) {
|
|
3936
4050
|
try {
|
|
3937
|
-
const mcpJson = JSON.parse(
|
|
4051
|
+
const mcpJson = JSON.parse(fs10.readFileSync(mcpJsonPath, "utf-8"));
|
|
3938
4052
|
if (mcpJson.mcpServers) {
|
|
3939
4053
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
3940
4054
|
items.push({
|
|
@@ -3951,7 +4065,7 @@ function scanLocalState(dir) {
|
|
|
3951
4065
|
}
|
|
3952
4066
|
}
|
|
3953
4067
|
const agentsMdPath = path9.join(dir, "AGENTS.md");
|
|
3954
|
-
if (
|
|
4068
|
+
if (fs10.existsSync(agentsMdPath)) {
|
|
3955
4069
|
items.push({
|
|
3956
4070
|
type: "rule",
|
|
3957
4071
|
platform: "codex",
|
|
@@ -3961,11 +4075,11 @@ function scanLocalState(dir) {
|
|
|
3961
4075
|
});
|
|
3962
4076
|
}
|
|
3963
4077
|
const codexSkillsDir = path9.join(dir, ".agents", "skills");
|
|
3964
|
-
if (
|
|
4078
|
+
if (fs10.existsSync(codexSkillsDir)) {
|
|
3965
4079
|
try {
|
|
3966
|
-
for (const name of
|
|
4080
|
+
for (const name of fs10.readdirSync(codexSkillsDir)) {
|
|
3967
4081
|
const skillFile = path9.join(codexSkillsDir, name, "SKILL.md");
|
|
3968
|
-
if (
|
|
4082
|
+
if (fs10.existsSync(skillFile)) {
|
|
3969
4083
|
items.push({
|
|
3970
4084
|
type: "skill",
|
|
3971
4085
|
platform: "codex",
|
|
@@ -3980,11 +4094,11 @@ function scanLocalState(dir) {
|
|
|
3980
4094
|
}
|
|
3981
4095
|
}
|
|
3982
4096
|
const opencodeSkillsDir = path9.join(dir, ".opencode", "skills");
|
|
3983
|
-
if (
|
|
4097
|
+
if (fs10.existsSync(opencodeSkillsDir)) {
|
|
3984
4098
|
try {
|
|
3985
|
-
for (const name of
|
|
4099
|
+
for (const name of fs10.readdirSync(opencodeSkillsDir)) {
|
|
3986
4100
|
const skillFile = path9.join(opencodeSkillsDir, name, "SKILL.md");
|
|
3987
|
-
if (
|
|
4101
|
+
if (fs10.existsSync(skillFile)) {
|
|
3988
4102
|
items.push({
|
|
3989
4103
|
type: "skill",
|
|
3990
4104
|
platform: "opencode",
|
|
@@ -3999,7 +4113,7 @@ function scanLocalState(dir) {
|
|
|
3999
4113
|
}
|
|
4000
4114
|
}
|
|
4001
4115
|
const cursorrulesPath = path9.join(dir, ".cursorrules");
|
|
4002
|
-
if (
|
|
4116
|
+
if (fs10.existsSync(cursorrulesPath)) {
|
|
4003
4117
|
items.push({
|
|
4004
4118
|
type: "rule",
|
|
4005
4119
|
platform: "cursor",
|
|
@@ -4009,8 +4123,8 @@ function scanLocalState(dir) {
|
|
|
4009
4123
|
});
|
|
4010
4124
|
}
|
|
4011
4125
|
const cursorRulesDir = path9.join(dir, ".cursor", "rules");
|
|
4012
|
-
if (
|
|
4013
|
-
for (const file of
|
|
4126
|
+
if (fs10.existsSync(cursorRulesDir)) {
|
|
4127
|
+
for (const file of fs10.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
4014
4128
|
const filePath = path9.join(cursorRulesDir, file);
|
|
4015
4129
|
items.push({
|
|
4016
4130
|
type: "rule",
|
|
@@ -4022,11 +4136,11 @@ function scanLocalState(dir) {
|
|
|
4022
4136
|
}
|
|
4023
4137
|
}
|
|
4024
4138
|
const cursorSkillsDir = path9.join(dir, ".cursor", "skills");
|
|
4025
|
-
if (
|
|
4139
|
+
if (fs10.existsSync(cursorSkillsDir)) {
|
|
4026
4140
|
try {
|
|
4027
|
-
for (const name of
|
|
4141
|
+
for (const name of fs10.readdirSync(cursorSkillsDir)) {
|
|
4028
4142
|
const skillFile = path9.join(cursorSkillsDir, name, "SKILL.md");
|
|
4029
|
-
if (
|
|
4143
|
+
if (fs10.existsSync(skillFile)) {
|
|
4030
4144
|
items.push({
|
|
4031
4145
|
type: "skill",
|
|
4032
4146
|
platform: "cursor",
|
|
@@ -4041,9 +4155,9 @@ function scanLocalState(dir) {
|
|
|
4041
4155
|
}
|
|
4042
4156
|
}
|
|
4043
4157
|
const cursorMcpPath = path9.join(dir, ".cursor", "mcp.json");
|
|
4044
|
-
if (
|
|
4158
|
+
if (fs10.existsSync(cursorMcpPath)) {
|
|
4045
4159
|
try {
|
|
4046
|
-
const mcpJson = JSON.parse(
|
|
4160
|
+
const mcpJson = JSON.parse(fs10.readFileSync(cursorMcpPath, "utf-8"));
|
|
4047
4161
|
if (mcpJson.mcpServers) {
|
|
4048
4162
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
4049
4163
|
items.push({
|
|
@@ -4062,7 +4176,7 @@ function scanLocalState(dir) {
|
|
|
4062
4176
|
return items;
|
|
4063
4177
|
}
|
|
4064
4178
|
function hashFile(filePath) {
|
|
4065
|
-
const text =
|
|
4179
|
+
const text = fs10.readFileSync(filePath, "utf-8");
|
|
4066
4180
|
return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
4067
4181
|
}
|
|
4068
4182
|
function hashJson(obj) {
|
|
@@ -4085,7 +4199,7 @@ function getCursorConfigDir() {
|
|
|
4085
4199
|
|
|
4086
4200
|
// src/lib/hooks.ts
|
|
4087
4201
|
init_resolve_caliber();
|
|
4088
|
-
import
|
|
4202
|
+
import fs11 from "fs";
|
|
4089
4203
|
import path10 from "path";
|
|
4090
4204
|
import { execSync as execSync8 } from "child_process";
|
|
4091
4205
|
var SETTINGS_PATH = path10.join(".claude", "settings.json");
|
|
@@ -4095,17 +4209,17 @@ function getHookCommand() {
|
|
|
4095
4209
|
return `${resolveCaliber()} ${REFRESH_TAIL}`;
|
|
4096
4210
|
}
|
|
4097
4211
|
function readSettings() {
|
|
4098
|
-
if (!
|
|
4212
|
+
if (!fs11.existsSync(SETTINGS_PATH)) return {};
|
|
4099
4213
|
try {
|
|
4100
|
-
return JSON.parse(
|
|
4214
|
+
return JSON.parse(fs11.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
4101
4215
|
} catch {
|
|
4102
4216
|
return {};
|
|
4103
4217
|
}
|
|
4104
4218
|
}
|
|
4105
4219
|
function writeSettings(settings) {
|
|
4106
4220
|
const dir = path10.dirname(SETTINGS_PATH);
|
|
4107
|
-
if (!
|
|
4108
|
-
|
|
4221
|
+
if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
|
|
4222
|
+
fs11.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
4109
4223
|
}
|
|
4110
4224
|
function findHookIndex(sessionEnd) {
|
|
4111
4225
|
return sessionEnd.findIndex(
|
|
@@ -4153,7 +4267,8 @@ function removeHook() {
|
|
|
4153
4267
|
return { removed: true, notFound: false };
|
|
4154
4268
|
}
|
|
4155
4269
|
function createScriptHook(config) {
|
|
4156
|
-
const { eventName, scriptPath,
|
|
4270
|
+
const { eventName, scriptPath, description } = config;
|
|
4271
|
+
const getContent = () => typeof config.scriptContent === "function" ? config.scriptContent() : config.scriptContent;
|
|
4157
4272
|
const hasHook = (matchers) => matchers.some((entry) => entry.hooks?.some((h) => h.description === description));
|
|
4158
4273
|
function isInstalled() {
|
|
4159
4274
|
const settings = readSettings();
|
|
@@ -4168,9 +4283,9 @@ function createScriptHook(config) {
|
|
|
4168
4283
|
return { installed: false, alreadyInstalled: true };
|
|
4169
4284
|
}
|
|
4170
4285
|
const scriptDir = path10.dirname(scriptPath);
|
|
4171
|
-
if (!
|
|
4172
|
-
|
|
4173
|
-
|
|
4286
|
+
if (!fs11.existsSync(scriptDir)) fs11.mkdirSync(scriptDir, { recursive: true });
|
|
4287
|
+
fs11.writeFileSync(scriptPath, getContent());
|
|
4288
|
+
fs11.chmodSync(scriptPath, 493);
|
|
4174
4289
|
if (!Array.isArray(settings.hooks[eventName])) {
|
|
4175
4290
|
settings.hooks[eventName] = [];
|
|
4176
4291
|
}
|
|
@@ -4194,7 +4309,7 @@ function createScriptHook(config) {
|
|
|
4194
4309
|
if (settings.hooks && Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
4195
4310
|
writeSettings(settings);
|
|
4196
4311
|
try {
|
|
4197
|
-
|
|
4312
|
+
fs11.unlinkSync(scriptPath);
|
|
4198
4313
|
} catch {
|
|
4199
4314
|
}
|
|
4200
4315
|
return { removed: true, notFound: false };
|
|
@@ -4221,7 +4336,9 @@ var stopHook = createScriptHook({
|
|
|
4221
4336
|
});
|
|
4222
4337
|
var installStopHook = stopHook.install;
|
|
4223
4338
|
var removeStopHook = stopHook.remove;
|
|
4224
|
-
|
|
4339
|
+
function getFreshnessScript() {
|
|
4340
|
+
const bin = resolveCaliber();
|
|
4341
|
+
return `#!/bin/sh
|
|
4225
4342
|
STATE_FILE=".caliber/.caliber-state.json"
|
|
4226
4343
|
[ ! -f "$STATE_FILE" ] && exit 0
|
|
4227
4344
|
LAST_SHA=$(grep -o '"lastRefreshSha":"[^"]*"' "$STATE_FILE" 2>/dev/null | cut -d'"' -f4)
|
|
@@ -4230,13 +4347,14 @@ CURRENT_SHA=$(git rev-parse HEAD 2>/dev/null)
|
|
|
4230
4347
|
[ "$LAST_SHA" = "$CURRENT_SHA" ] && exit 0
|
|
4231
4348
|
COMMITS_BEHIND=$(git rev-list --count "$LAST_SHA".."$CURRENT_SHA" 2>/dev/null || echo 0)
|
|
4232
4349
|
if [ "$COMMITS_BEHIND" -gt 15 ]; then
|
|
4233
|
-
printf '{"systemMessage":"Caliber: agent configs are %s commits behind. Run
|
|
4350
|
+
printf '{"systemMessage":"Caliber: agent configs are %s commits behind. Run ${bin} refresh to sync."}' "$COMMITS_BEHIND"
|
|
4234
4351
|
fi
|
|
4235
4352
|
`;
|
|
4353
|
+
}
|
|
4236
4354
|
var sessionStartHook = createScriptHook({
|
|
4237
4355
|
eventName: "SessionStart",
|
|
4238
4356
|
scriptPath: path10.join(".claude", "hooks", "caliber-session-freshness.sh"),
|
|
4239
|
-
scriptContent:
|
|
4357
|
+
scriptContent: getFreshnessScript,
|
|
4240
4358
|
description: "Caliber: check config freshness on session start"
|
|
4241
4359
|
});
|
|
4242
4360
|
var isSessionStartHookInstalled = sessionStartHook.isInstalled;
|
|
@@ -4245,7 +4363,7 @@ var removeSessionStartHook = sessionStartHook.remove;
|
|
|
4245
4363
|
var notificationHook = createScriptHook({
|
|
4246
4364
|
eventName: "Notification",
|
|
4247
4365
|
scriptPath: path10.join(".claude", "hooks", "caliber-freshness-notify.sh"),
|
|
4248
|
-
scriptContent:
|
|
4366
|
+
scriptContent: getFreshnessScript,
|
|
4249
4367
|
description: "Caliber: warn when agent configs are stale"
|
|
4250
4368
|
});
|
|
4251
4369
|
var isNotificationHookInstalled = notificationHook.isInstalled;
|
|
@@ -4254,10 +4372,28 @@ var removeNotificationHook = notificationHook.remove;
|
|
|
4254
4372
|
var PRECOMMIT_START = "# caliber:pre-commit:start";
|
|
4255
4373
|
var PRECOMMIT_END = "# caliber:pre-commit:end";
|
|
4256
4374
|
function getPrecommitBlock() {
|
|
4257
|
-
const
|
|
4375
|
+
const cmd = resolveCaliber();
|
|
4258
4376
|
const npx = isNpxResolution();
|
|
4259
|
-
|
|
4260
|
-
|
|
4377
|
+
let guard;
|
|
4378
|
+
let invoke;
|
|
4379
|
+
if (npx) {
|
|
4380
|
+
const npxBin = cmd.split(" ")[0];
|
|
4381
|
+
if (npxBin.startsWith("/")) {
|
|
4382
|
+
guard = `[ -x "${npxBin}" ]`;
|
|
4383
|
+
const npxArgs = cmd.slice(npxBin.length);
|
|
4384
|
+
invoke = `"${npxBin}"${npxArgs}`;
|
|
4385
|
+
} else {
|
|
4386
|
+
guard = "command -v npx >/dev/null 2>&1";
|
|
4387
|
+
invoke = cmd;
|
|
4388
|
+
}
|
|
4389
|
+
} else {
|
|
4390
|
+
if (cmd.startsWith("/")) {
|
|
4391
|
+
guard = `[ -x "${cmd}" ]`;
|
|
4392
|
+
} else {
|
|
4393
|
+
guard = `[ -x "${cmd}" ] || command -v "${cmd}" >/dev/null 2>&1`;
|
|
4394
|
+
}
|
|
4395
|
+
invoke = `"${cmd}"`;
|
|
4396
|
+
}
|
|
4261
4397
|
return `${PRECOMMIT_START}
|
|
4262
4398
|
if ${guard}; then
|
|
4263
4399
|
mkdir -p .caliber
|
|
@@ -4285,8 +4421,8 @@ function getPreCommitPath() {
|
|
|
4285
4421
|
}
|
|
4286
4422
|
function isPreCommitHookInstalled() {
|
|
4287
4423
|
const hookPath = getPreCommitPath();
|
|
4288
|
-
if (!hookPath || !
|
|
4289
|
-
const content =
|
|
4424
|
+
if (!hookPath || !fs11.existsSync(hookPath)) return false;
|
|
4425
|
+
const content = fs11.readFileSync(hookPath, "utf-8");
|
|
4290
4426
|
return content.includes(PRECOMMIT_START);
|
|
4291
4427
|
}
|
|
4292
4428
|
function installPreCommitHook() {
|
|
@@ -4296,45 +4432,45 @@ function installPreCommitHook() {
|
|
|
4296
4432
|
const hookPath = getPreCommitPath();
|
|
4297
4433
|
if (!hookPath) return { installed: false, alreadyInstalled: false };
|
|
4298
4434
|
const hooksDir = path10.dirname(hookPath);
|
|
4299
|
-
if (!
|
|
4435
|
+
if (!fs11.existsSync(hooksDir)) fs11.mkdirSync(hooksDir, { recursive: true });
|
|
4300
4436
|
let content = "";
|
|
4301
|
-
if (
|
|
4302
|
-
content =
|
|
4437
|
+
if (fs11.existsSync(hookPath)) {
|
|
4438
|
+
content = fs11.readFileSync(hookPath, "utf-8");
|
|
4303
4439
|
if (!content.endsWith("\n")) content += "\n";
|
|
4304
4440
|
content += "\n" + getPrecommitBlock() + "\n";
|
|
4305
4441
|
} else {
|
|
4306
4442
|
content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
|
|
4307
4443
|
}
|
|
4308
|
-
|
|
4309
|
-
|
|
4444
|
+
fs11.writeFileSync(hookPath, content);
|
|
4445
|
+
fs11.chmodSync(hookPath, 493);
|
|
4310
4446
|
return { installed: true, alreadyInstalled: false };
|
|
4311
4447
|
}
|
|
4312
4448
|
function removePreCommitHook() {
|
|
4313
4449
|
const hookPath = getPreCommitPath();
|
|
4314
|
-
if (!hookPath || !
|
|
4450
|
+
if (!hookPath || !fs11.existsSync(hookPath)) {
|
|
4315
4451
|
return { removed: false, notFound: true };
|
|
4316
4452
|
}
|
|
4317
|
-
let content =
|
|
4453
|
+
let content = fs11.readFileSync(hookPath, "utf-8");
|
|
4318
4454
|
if (!content.includes(PRECOMMIT_START)) {
|
|
4319
4455
|
return { removed: false, notFound: true };
|
|
4320
4456
|
}
|
|
4321
4457
|
const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
|
|
4322
4458
|
content = content.replace(regex, "\n");
|
|
4323
4459
|
if (content.trim() === "#!/bin/sh" || content.trim() === "") {
|
|
4324
|
-
|
|
4460
|
+
fs11.unlinkSync(hookPath);
|
|
4325
4461
|
} else {
|
|
4326
|
-
|
|
4462
|
+
fs11.writeFileSync(hookPath, content);
|
|
4327
4463
|
}
|
|
4328
4464
|
return { removed: true, notFound: false };
|
|
4329
4465
|
}
|
|
4330
4466
|
|
|
4331
4467
|
// src/fingerprint/sources.ts
|
|
4332
|
-
import
|
|
4468
|
+
import fs12 from "fs";
|
|
4333
4469
|
import path11 from "path";
|
|
4334
4470
|
|
|
4335
4471
|
// src/scoring/utils.ts
|
|
4336
4472
|
import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
|
|
4337
|
-
import { execFileSync } from "child_process";
|
|
4473
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
4338
4474
|
import { join, relative } from "path";
|
|
4339
4475
|
function readFileOrNull(filePath) {
|
|
4340
4476
|
try {
|
|
@@ -4384,7 +4520,7 @@ var IGNORED_FILES = /* @__PURE__ */ new Set([
|
|
|
4384
4520
|
]);
|
|
4385
4521
|
function isGitRepo2(dir) {
|
|
4386
4522
|
try {
|
|
4387
|
-
|
|
4523
|
+
execFileSync3("git", ["rev-parse", "--git-dir"], {
|
|
4388
4524
|
cwd: dir,
|
|
4389
4525
|
encoding: "utf-8",
|
|
4390
4526
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4397,7 +4533,7 @@ function isGitRepo2(dir) {
|
|
|
4397
4533
|
function checkGitIgnored(dir, paths) {
|
|
4398
4534
|
if (paths.length === 0) return /* @__PURE__ */ new Set();
|
|
4399
4535
|
try {
|
|
4400
|
-
const result =
|
|
4536
|
+
const result = execFileSync3("git", ["check-ignore", ...paths], {
|
|
4401
4537
|
cwd: dir,
|
|
4402
4538
|
encoding: "utf-8",
|
|
4403
4539
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4678,15 +4814,15 @@ function loadSourcesConfig(dir) {
|
|
|
4678
4814
|
}
|
|
4679
4815
|
function writeSourcesConfig(dir, sources2) {
|
|
4680
4816
|
const configDir = path11.join(dir, ".caliber");
|
|
4681
|
-
if (!
|
|
4682
|
-
|
|
4817
|
+
if (!fs12.existsSync(configDir)) {
|
|
4818
|
+
fs12.mkdirSync(configDir, { recursive: true });
|
|
4683
4819
|
}
|
|
4684
4820
|
const configPath = path11.join(configDir, "sources.json");
|
|
4685
|
-
|
|
4821
|
+
fs12.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
|
|
4686
4822
|
}
|
|
4687
4823
|
function detectSourceType(absPath) {
|
|
4688
4824
|
try {
|
|
4689
|
-
return
|
|
4825
|
+
return fs12.statSync(absPath).isDirectory() ? "repo" : "file";
|
|
4690
4826
|
} catch {
|
|
4691
4827
|
return "file";
|
|
4692
4828
|
}
|
|
@@ -4730,7 +4866,7 @@ function resolveAllSources(dir, cliSources, workspaces) {
|
|
|
4730
4866
|
for (const [absPath, resolved] of seen) {
|
|
4731
4867
|
let stat;
|
|
4732
4868
|
try {
|
|
4733
|
-
stat =
|
|
4869
|
+
stat = fs12.statSync(absPath);
|
|
4734
4870
|
} catch {
|
|
4735
4871
|
console.warn(`Source ${resolved.config.path || absPath} not found, skipping`);
|
|
4736
4872
|
continue;
|
|
@@ -4785,7 +4921,7 @@ function collectRepoSummary(resolved, _projectDir) {
|
|
|
4785
4921
|
let topLevelDirs;
|
|
4786
4922
|
let keyFiles;
|
|
4787
4923
|
try {
|
|
4788
|
-
const entries =
|
|
4924
|
+
const entries = fs12.readdirSync(absPath, { withFileTypes: true });
|
|
4789
4925
|
topLevelDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name).slice(0, 20);
|
|
4790
4926
|
keyFiles = entries.filter((e) => e.isFile() && !e.name.startsWith(".")).map((e) => e.name).slice(0, 15);
|
|
4791
4927
|
} catch {
|
|
@@ -5396,6 +5532,13 @@ var LIMITS = {
|
|
|
5396
5532
|
SKILL_CHARS: 3e3,
|
|
5397
5533
|
RULES_MAX: 10
|
|
5398
5534
|
};
|
|
5535
|
+
var BUILD_GENERATE_PROMPT_MAX_TOKENS = 12e4;
|
|
5536
|
+
var PROJECT_FILES_HEADER_RESERVE_TOKENS = 160;
|
|
5537
|
+
function maxCharsForCodeFileContent(runningJoinedLen, pathLine, budgetTokens) {
|
|
5538
|
+
const maxTotalChars = budgetTokens * 4;
|
|
5539
|
+
const overhead = runningJoinedLen + 1 + pathLine.length + 1;
|
|
5540
|
+
return Math.max(0, maxTotalChars - overhead);
|
|
5541
|
+
}
|
|
5399
5542
|
function truncate(text, maxChars) {
|
|
5400
5543
|
if (text.length <= maxChars) return text;
|
|
5401
5544
|
return text.slice(0, maxChars) + `
|
|
@@ -5601,9 +5744,12 @@ User instructions: ${prompt}`);
|
|
|
5601
5744
|
if (fingerprint.codeAnalysis) {
|
|
5602
5745
|
const ca = fingerprint.codeAnalysis;
|
|
5603
5746
|
const basePrompt = parts.join("\n");
|
|
5604
|
-
const
|
|
5747
|
+
const effectiveMaxTokens = Math.min(getMaxPromptTokens(), BUILD_GENERATE_PROMPT_MAX_TOKENS);
|
|
5605
5748
|
const baseTokens = estimateTokens(basePrompt);
|
|
5606
|
-
const tokenBudgetForCode = Math.max(
|
|
5749
|
+
const tokenBudgetForCode = Math.max(
|
|
5750
|
+
0,
|
|
5751
|
+
effectiveMaxTokens - baseTokens - PROJECT_FILES_HEADER_RESERVE_TOKENS
|
|
5752
|
+
);
|
|
5607
5753
|
const codeLines = [];
|
|
5608
5754
|
let codeChars = 0;
|
|
5609
5755
|
const introLine = "Study these files to extract patterns for skills. Use the exact code patterns you see here.\n";
|
|
@@ -5612,13 +5758,24 @@ User instructions: ${prompt}`);
|
|
|
5612
5758
|
const sortedFiles = [...ca.files].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
5613
5759
|
let includedFiles = 0;
|
|
5614
5760
|
for (const f of sortedFiles) {
|
|
5615
|
-
const
|
|
5616
|
-
|
|
5761
|
+
const pathLine = `[${f.path}]
|
|
5762
|
+
`;
|
|
5763
|
+
const maxContent = maxCharsForCodeFileContent(runningCodeLen, pathLine, tokenBudgetForCode);
|
|
5764
|
+
if (maxContent < 1) {
|
|
5765
|
+
if (includedFiles > 0) break;
|
|
5766
|
+
continue;
|
|
5767
|
+
}
|
|
5768
|
+
const content = f.content.slice(0, Math.min(f.content.length, maxContent));
|
|
5769
|
+
const entry = `${pathLine}${content}
|
|
5617
5770
|
`;
|
|
5618
5771
|
const projectedLen = runningCodeLen + 1 + entry.length;
|
|
5619
|
-
|
|
5772
|
+
const projectedTokens = Math.ceil(projectedLen / 4);
|
|
5773
|
+
if (projectedTokens > tokenBudgetForCode) {
|
|
5774
|
+
if (includedFiles > 0) break;
|
|
5775
|
+
continue;
|
|
5776
|
+
}
|
|
5620
5777
|
codeLines.push(entry);
|
|
5621
|
-
codeChars +=
|
|
5778
|
+
codeChars += content.length;
|
|
5622
5779
|
runningCodeLen = projectedLen;
|
|
5623
5780
|
includedFiles++;
|
|
5624
5781
|
}
|
|
@@ -5646,32 +5803,32 @@ ${f.content}
|
|
|
5646
5803
|
}
|
|
5647
5804
|
|
|
5648
5805
|
// src/writers/index.ts
|
|
5649
|
-
import
|
|
5806
|
+
import fs21 from "fs";
|
|
5650
5807
|
|
|
5651
5808
|
// src/writers/claude/index.ts
|
|
5652
5809
|
init_pre_commit_block();
|
|
5653
|
-
import
|
|
5810
|
+
import fs13 from "fs";
|
|
5654
5811
|
import path12 from "path";
|
|
5655
5812
|
function writeClaudeConfig(config) {
|
|
5656
5813
|
const written = [];
|
|
5657
|
-
|
|
5814
|
+
fs13.writeFileSync(
|
|
5658
5815
|
"CLAUDE.md",
|
|
5659
|
-
|
|
5816
|
+
appendManagedBlocks(config.claudeMd)
|
|
5660
5817
|
);
|
|
5661
5818
|
written.push("CLAUDE.md");
|
|
5662
5819
|
if (config.rules?.length) {
|
|
5663
5820
|
const rulesDir = path12.join(".claude", "rules");
|
|
5664
|
-
if (!
|
|
5821
|
+
if (!fs13.existsSync(rulesDir)) fs13.mkdirSync(rulesDir, { recursive: true });
|
|
5665
5822
|
for (const rule of config.rules) {
|
|
5666
5823
|
const rulePath = path12.join(rulesDir, rule.filename);
|
|
5667
|
-
|
|
5824
|
+
fs13.writeFileSync(rulePath, rule.content);
|
|
5668
5825
|
written.push(rulePath);
|
|
5669
5826
|
}
|
|
5670
5827
|
}
|
|
5671
5828
|
if (config.skills?.length) {
|
|
5672
5829
|
for (const skill of config.skills) {
|
|
5673
5830
|
const skillDir = path12.join(".claude", "skills", skill.name);
|
|
5674
|
-
if (!
|
|
5831
|
+
if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
|
|
5675
5832
|
const skillPath = path12.join(skillDir, "SKILL.md");
|
|
5676
5833
|
const frontmatterLines = ["---", `name: ${skill.name}`, `description: ${skill.description}`];
|
|
5677
5834
|
if (skill.paths?.length) {
|
|
@@ -5682,21 +5839,21 @@ function writeClaudeConfig(config) {
|
|
|
5682
5839
|
}
|
|
5683
5840
|
frontmatterLines.push("---", "");
|
|
5684
5841
|
const frontmatter = frontmatterLines.join("\n");
|
|
5685
|
-
|
|
5842
|
+
fs13.writeFileSync(skillPath, frontmatter + skill.content);
|
|
5686
5843
|
written.push(skillPath);
|
|
5687
5844
|
}
|
|
5688
5845
|
}
|
|
5689
5846
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
5690
5847
|
let existingServers = {};
|
|
5691
5848
|
try {
|
|
5692
|
-
if (
|
|
5693
|
-
const existing = JSON.parse(
|
|
5849
|
+
if (fs13.existsSync(".mcp.json")) {
|
|
5850
|
+
const existing = JSON.parse(fs13.readFileSync(".mcp.json", "utf-8"));
|
|
5694
5851
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
5695
5852
|
}
|
|
5696
5853
|
} catch {
|
|
5697
5854
|
}
|
|
5698
5855
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
5699
|
-
|
|
5856
|
+
fs13.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
5700
5857
|
written.push(".mcp.json");
|
|
5701
5858
|
}
|
|
5702
5859
|
return written;
|
|
@@ -5704,12 +5861,12 @@ function writeClaudeConfig(config) {
|
|
|
5704
5861
|
|
|
5705
5862
|
// src/writers/cursor/index.ts
|
|
5706
5863
|
init_pre_commit_block();
|
|
5707
|
-
import
|
|
5864
|
+
import fs14 from "fs";
|
|
5708
5865
|
import path13 from "path";
|
|
5709
5866
|
function writeCursorConfig(config) {
|
|
5710
5867
|
const written = [];
|
|
5711
5868
|
if (config.cursorrules) {
|
|
5712
|
-
|
|
5869
|
+
fs14.writeFileSync(".cursorrules", config.cursorrules);
|
|
5713
5870
|
written.push(".cursorrules");
|
|
5714
5871
|
}
|
|
5715
5872
|
const preCommitRule = getCursorPreCommitRule();
|
|
@@ -5717,16 +5874,16 @@ function writeCursorConfig(config) {
|
|
|
5717
5874
|
const syncRule = getCursorSyncRule();
|
|
5718
5875
|
const allRules = [...config.rules || [], preCommitRule, learningsRule, syncRule];
|
|
5719
5876
|
const rulesDir = path13.join(".cursor", "rules");
|
|
5720
|
-
if (!
|
|
5877
|
+
if (!fs14.existsSync(rulesDir)) fs14.mkdirSync(rulesDir, { recursive: true });
|
|
5721
5878
|
for (const rule of allRules) {
|
|
5722
5879
|
const rulePath = path13.join(rulesDir, rule.filename);
|
|
5723
|
-
|
|
5880
|
+
fs14.writeFileSync(rulePath, rule.content);
|
|
5724
5881
|
written.push(rulePath);
|
|
5725
5882
|
}
|
|
5726
5883
|
if (config.skills?.length) {
|
|
5727
5884
|
for (const skill of config.skills) {
|
|
5728
5885
|
const skillDir = path13.join(".cursor", "skills", skill.name);
|
|
5729
|
-
if (!
|
|
5886
|
+
if (!fs14.existsSync(skillDir)) fs14.mkdirSync(skillDir, { recursive: true });
|
|
5730
5887
|
const skillPath = path13.join(skillDir, "SKILL.md");
|
|
5731
5888
|
const frontmatter = [
|
|
5732
5889
|
"---",
|
|
@@ -5735,24 +5892,24 @@ function writeCursorConfig(config) {
|
|
|
5735
5892
|
"---",
|
|
5736
5893
|
""
|
|
5737
5894
|
].join("\n");
|
|
5738
|
-
|
|
5895
|
+
fs14.writeFileSync(skillPath, frontmatter + skill.content);
|
|
5739
5896
|
written.push(skillPath);
|
|
5740
5897
|
}
|
|
5741
5898
|
}
|
|
5742
5899
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
5743
5900
|
const cursorDir = ".cursor";
|
|
5744
|
-
if (!
|
|
5901
|
+
if (!fs14.existsSync(cursorDir)) fs14.mkdirSync(cursorDir, { recursive: true });
|
|
5745
5902
|
const mcpPath = path13.join(cursorDir, "mcp.json");
|
|
5746
5903
|
let existingServers = {};
|
|
5747
5904
|
try {
|
|
5748
|
-
if (
|
|
5749
|
-
const existing = JSON.parse(
|
|
5905
|
+
if (fs14.existsSync(mcpPath)) {
|
|
5906
|
+
const existing = JSON.parse(fs14.readFileSync(mcpPath, "utf-8"));
|
|
5750
5907
|
if (existing.mcpServers) existingServers = existing.mcpServers;
|
|
5751
5908
|
}
|
|
5752
5909
|
} catch {
|
|
5753
5910
|
}
|
|
5754
5911
|
const mergedServers = { ...existingServers, ...config.mcpServers };
|
|
5755
|
-
|
|
5912
|
+
fs14.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
|
|
5756
5913
|
written.push(mcpPath);
|
|
5757
5914
|
}
|
|
5758
5915
|
return written;
|
|
@@ -5760,19 +5917,19 @@ function writeCursorConfig(config) {
|
|
|
5760
5917
|
|
|
5761
5918
|
// src/writers/codex/index.ts
|
|
5762
5919
|
init_pre_commit_block();
|
|
5763
|
-
import
|
|
5920
|
+
import fs15 from "fs";
|
|
5764
5921
|
import path14 from "path";
|
|
5765
5922
|
function writeCodexConfig(config) {
|
|
5766
5923
|
const written = [];
|
|
5767
|
-
|
|
5924
|
+
fs15.writeFileSync(
|
|
5768
5925
|
"AGENTS.md",
|
|
5769
|
-
|
|
5926
|
+
appendManagedBlocks(config.agentsMd, "codex")
|
|
5770
5927
|
);
|
|
5771
5928
|
written.push("AGENTS.md");
|
|
5772
5929
|
if (config.skills?.length) {
|
|
5773
5930
|
for (const skill of config.skills) {
|
|
5774
5931
|
const skillDir = path14.join(".agents", "skills", skill.name);
|
|
5775
|
-
if (!
|
|
5932
|
+
if (!fs15.existsSync(skillDir)) fs15.mkdirSync(skillDir, { recursive: true });
|
|
5776
5933
|
const skillPath = path14.join(skillDir, "SKILL.md");
|
|
5777
5934
|
const frontmatter = [
|
|
5778
5935
|
"---",
|
|
@@ -5781,7 +5938,7 @@ function writeCodexConfig(config) {
|
|
|
5781
5938
|
"---",
|
|
5782
5939
|
""
|
|
5783
5940
|
].join("\n");
|
|
5784
|
-
|
|
5941
|
+
fs15.writeFileSync(skillPath, frontmatter + skill.content);
|
|
5785
5942
|
written.push(skillPath);
|
|
5786
5943
|
}
|
|
5787
5944
|
}
|
|
@@ -5790,23 +5947,23 @@ function writeCodexConfig(config) {
|
|
|
5790
5947
|
|
|
5791
5948
|
// src/writers/github-copilot/index.ts
|
|
5792
5949
|
init_pre_commit_block();
|
|
5793
|
-
import
|
|
5950
|
+
import fs16 from "fs";
|
|
5794
5951
|
import path15 from "path";
|
|
5795
5952
|
function writeGithubCopilotConfig(config) {
|
|
5796
5953
|
const written = [];
|
|
5797
5954
|
if (config.instructions) {
|
|
5798
|
-
|
|
5799
|
-
|
|
5955
|
+
fs16.mkdirSync(".github", { recursive: true });
|
|
5956
|
+
fs16.writeFileSync(
|
|
5800
5957
|
path15.join(".github", "copilot-instructions.md"),
|
|
5801
|
-
|
|
5958
|
+
appendManagedBlocks(config.instructions, "copilot")
|
|
5802
5959
|
);
|
|
5803
5960
|
written.push(".github/copilot-instructions.md");
|
|
5804
5961
|
}
|
|
5805
5962
|
if (config.instructionFiles?.length) {
|
|
5806
5963
|
const instructionsDir = path15.join(".github", "instructions");
|
|
5807
|
-
|
|
5964
|
+
fs16.mkdirSync(instructionsDir, { recursive: true });
|
|
5808
5965
|
for (const file of config.instructionFiles) {
|
|
5809
|
-
|
|
5966
|
+
fs16.writeFileSync(path15.join(instructionsDir, file.filename), file.content);
|
|
5810
5967
|
written.push(`.github/instructions/${file.filename}`);
|
|
5811
5968
|
}
|
|
5812
5969
|
}
|
|
@@ -5816,12 +5973,12 @@ function writeGithubCopilotConfig(config) {
|
|
|
5816
5973
|
// src/writers/opencode/index.ts
|
|
5817
5974
|
init_pre_commit_block();
|
|
5818
5975
|
init_builtin_skills();
|
|
5819
|
-
import
|
|
5976
|
+
import fs18 from "fs";
|
|
5820
5977
|
import path17 from "path";
|
|
5821
5978
|
function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
|
|
5822
5979
|
const written = [];
|
|
5823
5980
|
if (!agentsMdAlreadyWritten) {
|
|
5824
|
-
|
|
5981
|
+
fs18.writeFileSync(
|
|
5825
5982
|
"AGENTS.md",
|
|
5826
5983
|
appendLearningsBlock(appendPreCommitBlock(config.agentsMd, "codex"))
|
|
5827
5984
|
);
|
|
@@ -5830,9 +5987,9 @@ function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
|
|
|
5830
5987
|
if (config.skills?.length) {
|
|
5831
5988
|
for (const skill of config.skills) {
|
|
5832
5989
|
const skillDir = path17.join(".opencode", "skills", skill.name);
|
|
5833
|
-
if (!
|
|
5990
|
+
if (!fs18.existsSync(skillDir)) fs18.mkdirSync(skillDir, { recursive: true });
|
|
5834
5991
|
const skillPath = path17.join(skillDir, "SKILL.md");
|
|
5835
|
-
|
|
5992
|
+
fs18.writeFileSync(skillPath, buildSkillContent(skill));
|
|
5836
5993
|
written.push(skillPath);
|
|
5837
5994
|
}
|
|
5838
5995
|
}
|
|
@@ -5840,30 +5997,30 @@ function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
|
|
|
5840
5997
|
}
|
|
5841
5998
|
|
|
5842
5999
|
// src/writers/backup.ts
|
|
5843
|
-
import
|
|
6000
|
+
import fs19 from "fs";
|
|
5844
6001
|
import path18 from "path";
|
|
5845
6002
|
function createBackup(files) {
|
|
5846
6003
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5847
6004
|
const backupDir = path18.join(BACKUPS_DIR, timestamp);
|
|
5848
6005
|
for (const file of files) {
|
|
5849
|
-
if (!
|
|
6006
|
+
if (!fs19.existsSync(file)) continue;
|
|
5850
6007
|
const dest = path18.join(backupDir, file);
|
|
5851
6008
|
const destDir = path18.dirname(dest);
|
|
5852
|
-
if (!
|
|
5853
|
-
|
|
6009
|
+
if (!fs19.existsSync(destDir)) {
|
|
6010
|
+
fs19.mkdirSync(destDir, { recursive: true });
|
|
5854
6011
|
}
|
|
5855
|
-
|
|
6012
|
+
fs19.copyFileSync(file, dest);
|
|
5856
6013
|
}
|
|
5857
6014
|
return backupDir;
|
|
5858
6015
|
}
|
|
5859
6016
|
function restoreBackup(backupDir, file) {
|
|
5860
6017
|
const backupFile = path18.join(backupDir, file);
|
|
5861
|
-
if (!
|
|
6018
|
+
if (!fs19.existsSync(backupFile)) return false;
|
|
5862
6019
|
const destDir = path18.dirname(file);
|
|
5863
|
-
if (!
|
|
5864
|
-
|
|
6020
|
+
if (!fs19.existsSync(destDir)) {
|
|
6021
|
+
fs19.mkdirSync(destDir, { recursive: true });
|
|
5865
6022
|
}
|
|
5866
|
-
|
|
6023
|
+
fs19.copyFileSync(backupFile, file);
|
|
5867
6024
|
return true;
|
|
5868
6025
|
}
|
|
5869
6026
|
|
|
@@ -5871,32 +6028,32 @@ function restoreBackup(backupDir, file) {
|
|
|
5871
6028
|
init_builtin_skills();
|
|
5872
6029
|
|
|
5873
6030
|
// src/writers/manifest.ts
|
|
5874
|
-
import
|
|
6031
|
+
import fs20 from "fs";
|
|
5875
6032
|
import crypto3 from "crypto";
|
|
5876
6033
|
function readManifest() {
|
|
5877
6034
|
try {
|
|
5878
|
-
if (!
|
|
5879
|
-
return JSON.parse(
|
|
6035
|
+
if (!fs20.existsSync(MANIFEST_FILE)) return null;
|
|
6036
|
+
return JSON.parse(fs20.readFileSync(MANIFEST_FILE, "utf-8"));
|
|
5880
6037
|
} catch {
|
|
5881
6038
|
return null;
|
|
5882
6039
|
}
|
|
5883
6040
|
}
|
|
5884
6041
|
function writeManifest(manifest) {
|
|
5885
|
-
if (!
|
|
5886
|
-
|
|
6042
|
+
if (!fs20.existsSync(CALIBER_DIR)) {
|
|
6043
|
+
fs20.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
5887
6044
|
}
|
|
5888
|
-
|
|
6045
|
+
fs20.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
|
|
5889
6046
|
}
|
|
5890
6047
|
function fileChecksum(filePath) {
|
|
5891
|
-
const content =
|
|
6048
|
+
const content = fs20.readFileSync(filePath);
|
|
5892
6049
|
return crypto3.createHash("sha256").update(content).digest("hex");
|
|
5893
6050
|
}
|
|
5894
6051
|
|
|
5895
6052
|
// src/writers/index.ts
|
|
5896
6053
|
function writeSetup(setup) {
|
|
5897
6054
|
const filesToWrite = getFilesToWrite(setup);
|
|
5898
|
-
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) =>
|
|
5899
|
-
const existingFiles = [...filesToWrite.filter((f) =>
|
|
6055
|
+
const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs21.existsSync(f));
|
|
6056
|
+
const existingFiles = [...filesToWrite.filter((f) => fs21.existsSync(f)), ...filesToDelete];
|
|
5900
6057
|
const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
|
|
5901
6058
|
const written = [];
|
|
5902
6059
|
if (setup.targetAgent.includes("claude") && setup.claude) {
|
|
@@ -5917,7 +6074,7 @@ function writeSetup(setup) {
|
|
|
5917
6074
|
}
|
|
5918
6075
|
const deleted = [];
|
|
5919
6076
|
for (const filePath of filesToDelete) {
|
|
5920
|
-
|
|
6077
|
+
fs21.unlinkSync(filePath);
|
|
5921
6078
|
deleted.push(filePath);
|
|
5922
6079
|
}
|
|
5923
6080
|
written.push(...ensureBuiltinSkills());
|
|
@@ -5948,8 +6105,8 @@ function undoSetup() {
|
|
|
5948
6105
|
const removed = [];
|
|
5949
6106
|
for (const entry of manifest.entries) {
|
|
5950
6107
|
if (entry.action === "created") {
|
|
5951
|
-
if (
|
|
5952
|
-
|
|
6108
|
+
if (fs21.existsSync(entry.path)) {
|
|
6109
|
+
fs21.unlinkSync(entry.path);
|
|
5953
6110
|
removed.push(entry.path);
|
|
5954
6111
|
}
|
|
5955
6112
|
} else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
|
|
@@ -5958,8 +6115,8 @@ function undoSetup() {
|
|
|
5958
6115
|
}
|
|
5959
6116
|
}
|
|
5960
6117
|
}
|
|
5961
|
-
if (
|
|
5962
|
-
|
|
6118
|
+
if (fs21.existsSync(MANIFEST_FILE)) {
|
|
6119
|
+
fs21.unlinkSync(MANIFEST_FILE);
|
|
5963
6120
|
}
|
|
5964
6121
|
return { restored, removed };
|
|
5965
6122
|
}
|
|
@@ -6012,18 +6169,18 @@ function getFilesToWrite(setup) {
|
|
|
6012
6169
|
}
|
|
6013
6170
|
function ensureGitignore() {
|
|
6014
6171
|
const gitignorePath = ".gitignore";
|
|
6015
|
-
if (
|
|
6016
|
-
const content =
|
|
6172
|
+
if (fs21.existsSync(gitignorePath)) {
|
|
6173
|
+
const content = fs21.readFileSync(gitignorePath, "utf-8");
|
|
6017
6174
|
if (!content.includes(".caliber/")) {
|
|
6018
|
-
|
|
6175
|
+
fs21.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
|
|
6019
6176
|
}
|
|
6020
6177
|
} else {
|
|
6021
|
-
|
|
6178
|
+
fs21.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
|
|
6022
6179
|
}
|
|
6023
6180
|
}
|
|
6024
6181
|
|
|
6025
6182
|
// src/writers/staging.ts
|
|
6026
|
-
import
|
|
6183
|
+
import fs22 from "fs";
|
|
6027
6184
|
import path19 from "path";
|
|
6028
6185
|
var STAGED_DIR = path19.join(CALIBER_DIR, "staged");
|
|
6029
6186
|
var PROPOSED_DIR = path19.join(STAGED_DIR, "proposed");
|
|
@@ -6039,19 +6196,19 @@ function stageFiles(files, projectDir) {
|
|
|
6039
6196
|
for (const file of files) {
|
|
6040
6197
|
assertPathWithinDir(file.path, projectDir);
|
|
6041
6198
|
const originalPath = path19.join(projectDir, file.path);
|
|
6042
|
-
if (
|
|
6043
|
-
const existing =
|
|
6199
|
+
if (fs22.existsSync(originalPath)) {
|
|
6200
|
+
const existing = fs22.readFileSync(originalPath, "utf-8");
|
|
6044
6201
|
if (normalizeContent(existing) === normalizeContent(file.content)) {
|
|
6045
6202
|
continue;
|
|
6046
6203
|
}
|
|
6047
6204
|
}
|
|
6048
6205
|
const proposedPath = path19.join(PROPOSED_DIR, file.path);
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
if (
|
|
6206
|
+
fs22.mkdirSync(path19.dirname(proposedPath), { recursive: true });
|
|
6207
|
+
fs22.writeFileSync(proposedPath, file.content);
|
|
6208
|
+
if (fs22.existsSync(originalPath)) {
|
|
6052
6209
|
const currentPath = path19.join(CURRENT_DIR, file.path);
|
|
6053
|
-
|
|
6054
|
-
|
|
6210
|
+
fs22.mkdirSync(path19.dirname(currentPath), { recursive: true });
|
|
6211
|
+
fs22.copyFileSync(originalPath, currentPath);
|
|
6055
6212
|
modifiedFiles++;
|
|
6056
6213
|
stagedFiles.push({
|
|
6057
6214
|
relativePath: file.path,
|
|
@@ -6068,14 +6225,14 @@ function stageFiles(files, projectDir) {
|
|
|
6068
6225
|
return { newFiles, modifiedFiles, stagedFiles };
|
|
6069
6226
|
}
|
|
6070
6227
|
function cleanupStaging() {
|
|
6071
|
-
if (
|
|
6072
|
-
|
|
6228
|
+
if (fs22.existsSync(STAGED_DIR)) {
|
|
6229
|
+
fs22.rmSync(STAGED_DIR, { recursive: true, force: true });
|
|
6073
6230
|
}
|
|
6074
6231
|
}
|
|
6075
6232
|
|
|
6076
6233
|
// src/commands/setup-files.ts
|
|
6077
6234
|
init_builtin_skills();
|
|
6078
|
-
import
|
|
6235
|
+
import fs23 from "fs";
|
|
6079
6236
|
function collectSetupFiles(setup, targetAgent) {
|
|
6080
6237
|
const files = [];
|
|
6081
6238
|
const claude = setup.claude;
|
|
@@ -6182,7 +6339,7 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
6182
6339
|
}
|
|
6183
6340
|
const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
|
|
6184
6341
|
const opencodeTargeted = targetAgent ? targetAgent.includes("opencode") : false;
|
|
6185
|
-
if ((codexTargeted || opencodeTargeted) && !
|
|
6342
|
+
if ((codexTargeted || opencodeTargeted) && !fs23.existsSync("AGENTS.md") && !(codex && codex.agentsMd) && !(opencode && opencode.agentsMd)) {
|
|
6186
6343
|
const agentRefs = [];
|
|
6187
6344
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
6188
6345
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
@@ -6201,7 +6358,7 @@ ${agentRefs.join(" ")}
|
|
|
6201
6358
|
|
|
6202
6359
|
// src/lib/learning-hooks.ts
|
|
6203
6360
|
init_resolve_caliber();
|
|
6204
|
-
import
|
|
6361
|
+
import fs24 from "fs";
|
|
6205
6362
|
import path20 from "path";
|
|
6206
6363
|
var SETTINGS_PATH2 = path20.join(".claude", "settings.json");
|
|
6207
6364
|
var HOOK_TAILS = [
|
|
@@ -6220,17 +6377,17 @@ function getHookConfigs() {
|
|
|
6220
6377
|
}));
|
|
6221
6378
|
}
|
|
6222
6379
|
function readSettings2() {
|
|
6223
|
-
if (!
|
|
6380
|
+
if (!fs24.existsSync(SETTINGS_PATH2)) return {};
|
|
6224
6381
|
try {
|
|
6225
|
-
return JSON.parse(
|
|
6382
|
+
return JSON.parse(fs24.readFileSync(SETTINGS_PATH2, "utf-8"));
|
|
6226
6383
|
} catch {
|
|
6227
6384
|
return {};
|
|
6228
6385
|
}
|
|
6229
6386
|
}
|
|
6230
6387
|
function writeSettings2(settings) {
|
|
6231
6388
|
const dir = path20.dirname(SETTINGS_PATH2);
|
|
6232
|
-
if (!
|
|
6233
|
-
|
|
6389
|
+
if (!fs24.existsSync(dir)) fs24.mkdirSync(dir, { recursive: true });
|
|
6390
|
+
fs24.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
|
|
6234
6391
|
}
|
|
6235
6392
|
function hasLearningHook(matchers, tail) {
|
|
6236
6393
|
return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
|
|
@@ -6272,17 +6429,17 @@ var CURSOR_HOOK_EVENTS = [
|
|
|
6272
6429
|
{ event: "sessionEnd", tail: "learn finalize --auto" }
|
|
6273
6430
|
];
|
|
6274
6431
|
function readCursorHooks() {
|
|
6275
|
-
if (!
|
|
6432
|
+
if (!fs24.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
|
|
6276
6433
|
try {
|
|
6277
|
-
return JSON.parse(
|
|
6434
|
+
return JSON.parse(fs24.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
|
|
6278
6435
|
} catch {
|
|
6279
6436
|
return { version: 1, hooks: {} };
|
|
6280
6437
|
}
|
|
6281
6438
|
}
|
|
6282
6439
|
function writeCursorHooks(config) {
|
|
6283
6440
|
const dir = path20.dirname(CURSOR_HOOKS_PATH);
|
|
6284
|
-
if (!
|
|
6285
|
-
|
|
6441
|
+
if (!fs24.existsSync(dir)) fs24.mkdirSync(dir, { recursive: true });
|
|
6442
|
+
fs24.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
|
|
6286
6443
|
}
|
|
6287
6444
|
function hasCursorHook(entries, tail) {
|
|
6288
6445
|
return entries.some((e) => isCaliberCommand(e.command, tail));
|
|
@@ -6354,7 +6511,7 @@ function removeLearningHooks() {
|
|
|
6354
6511
|
init_resolve_caliber();
|
|
6355
6512
|
|
|
6356
6513
|
// src/lib/state.ts
|
|
6357
|
-
import
|
|
6514
|
+
import fs25 from "fs";
|
|
6358
6515
|
import path21 from "path";
|
|
6359
6516
|
import { execSync as execSync9 } from "child_process";
|
|
6360
6517
|
var STATE_FILE = path21.join(CALIBER_DIR, ".caliber-state.json");
|
|
@@ -6368,8 +6525,8 @@ function normalizeTargetAgent(value) {
|
|
|
6368
6525
|
}
|
|
6369
6526
|
function readState() {
|
|
6370
6527
|
try {
|
|
6371
|
-
if (!
|
|
6372
|
-
const raw = JSON.parse(
|
|
6528
|
+
if (!fs25.existsSync(STATE_FILE)) return null;
|
|
6529
|
+
const raw = JSON.parse(fs25.readFileSync(STATE_FILE, "utf-8"));
|
|
6373
6530
|
if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
|
|
6374
6531
|
return raw;
|
|
6375
6532
|
} catch {
|
|
@@ -6377,10 +6534,10 @@ function readState() {
|
|
|
6377
6534
|
}
|
|
6378
6535
|
}
|
|
6379
6536
|
function writeState(state) {
|
|
6380
|
-
if (!
|
|
6381
|
-
|
|
6537
|
+
if (!fs25.existsSync(CALIBER_DIR)) {
|
|
6538
|
+
fs25.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
6382
6539
|
}
|
|
6383
|
-
|
|
6540
|
+
fs25.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
6384
6541
|
}
|
|
6385
6542
|
function getCurrentHeadSha() {
|
|
6386
6543
|
try {
|
|
@@ -6397,6 +6554,7 @@ function getCurrentHeadSha() {
|
|
|
6397
6554
|
import chalk2 from "chalk";
|
|
6398
6555
|
import readline from "readline";
|
|
6399
6556
|
function promptInput(question) {
|
|
6557
|
+
if (!process.stdin.isTTY) return Promise.resolve("");
|
|
6400
6558
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
6401
6559
|
return new Promise((resolve3) => {
|
|
6402
6560
|
rl.question(chalk2.cyan(`${question} `), (answer) => {
|
|
@@ -6540,6 +6698,7 @@ var POINTS_FRESHNESS = 4;
|
|
|
6540
6698
|
var POINTS_NO_SECRETS = 4;
|
|
6541
6699
|
var POINTS_PERMISSIONS = 2;
|
|
6542
6700
|
var POINTS_HOOKS = 2;
|
|
6701
|
+
var POINTS_MODEL_PINNED = 2;
|
|
6543
6702
|
var POINTS_AGENTS_MD = 1;
|
|
6544
6703
|
var POINTS_OPEN_SKILLS_FORMAT = 2;
|
|
6545
6704
|
var POINTS_LEARNED_CONTENT = 2;
|
|
@@ -7324,6 +7483,19 @@ import { execSync as execSync12 } from "child_process";
|
|
|
7324
7483
|
import { join as join7 } from "path";
|
|
7325
7484
|
init_resolve_caliber();
|
|
7326
7485
|
init_pre_commit_block();
|
|
7486
|
+
|
|
7487
|
+
// src/scoring/model-pinning.ts
|
|
7488
|
+
function configContentSuggestsPinnedModel(lower) {
|
|
7489
|
+
if (/\bcaliber_model\b/.test(lower) || /\bcaliber_fast_model\b/.test(lower)) return true;
|
|
7490
|
+
if (/(?:^|[\s`'"\n])\/model(?:[\s`'"\n]|$)/.test(lower)) return true;
|
|
7491
|
+
if (/claude-(sonnet|opus|haiku)([-.@\d]|\b)/.test(lower)) return true;
|
|
7492
|
+
if (/\bgpt-[45]([-._\d]|\b)/.test(lower)) return true;
|
|
7493
|
+
if (/\bsonnet-4\.[\d.]+\b/.test(lower)) return true;
|
|
7494
|
+
if (/\b(high|medium|low)\s+effort\b/.test(lower)) return true;
|
|
7495
|
+
return false;
|
|
7496
|
+
}
|
|
7497
|
+
|
|
7498
|
+
// src/scoring/checks/bonus.ts
|
|
7327
7499
|
function hasPreCommitHook(dir) {
|
|
7328
7500
|
try {
|
|
7329
7501
|
const gitDir = execSync12("git rev-parse --git-dir", {
|
|
@@ -7444,6 +7616,33 @@ function checkBonus(dir) {
|
|
|
7444
7616
|
detail: hasLearned ? "Session learnings found in CALIBER_LEARNINGS.md" : "No learned content",
|
|
7445
7617
|
suggestion: hasLearned ? void 0 : `Session learnings capture patterns from your coding sessions so the agent improves over time. Run \`${resolveCaliber()} learn install\``
|
|
7446
7618
|
});
|
|
7619
|
+
const configContent = (() => {
|
|
7620
|
+
const parts = [];
|
|
7621
|
+
for (const rel of ["CLAUDE.md", "AGENTS.md"]) {
|
|
7622
|
+
const c = readFileOrNull(join7(dir, rel));
|
|
7623
|
+
if (c) parts.push(c);
|
|
7624
|
+
}
|
|
7625
|
+
try {
|
|
7626
|
+
const rulesDir = join7(dir, ".cursor", "rules");
|
|
7627
|
+
for (const f of readdirSync3(rulesDir).filter((x) => x.endsWith(".mdc"))) {
|
|
7628
|
+
const content = readFileOrNull(join7(rulesDir, f));
|
|
7629
|
+
if (content) parts.push(content);
|
|
7630
|
+
}
|
|
7631
|
+
} catch {
|
|
7632
|
+
}
|
|
7633
|
+
return parts.join("\n").toLowerCase();
|
|
7634
|
+
})();
|
|
7635
|
+
const hasModelRef = configContentSuggestsPinnedModel(configContent);
|
|
7636
|
+
checks.push({
|
|
7637
|
+
id: "model_pinned",
|
|
7638
|
+
name: "Model & effort pinned",
|
|
7639
|
+
category: "bonus",
|
|
7640
|
+
maxPoints: POINTS_MODEL_PINNED,
|
|
7641
|
+
earnedPoints: hasModelRef ? POINTS_MODEL_PINNED : 0,
|
|
7642
|
+
passed: hasModelRef,
|
|
7643
|
+
detail: hasModelRef ? "Model or effort level explicitly set in config" : "Config doesn't pin model or effort level \u2014 behavior may change when defaults are updated",
|
|
7644
|
+
suggestion: hasModelRef ? void 0 : "Add model/effort to config: CALIBER_MODEL env var, or /model in Claude Code, or a Model Configuration section in CLAUDE.md"
|
|
7645
|
+
});
|
|
7447
7646
|
return checks;
|
|
7448
7647
|
}
|
|
7449
7648
|
|
|
@@ -7487,7 +7686,7 @@ function checkSources(dir) {
|
|
|
7487
7686
|
}
|
|
7488
7687
|
|
|
7489
7688
|
// src/scoring/dismissed.ts
|
|
7490
|
-
import
|
|
7689
|
+
import fs26 from "fs";
|
|
7491
7690
|
import path22 from "path";
|
|
7492
7691
|
var DISMISSED_FILE = path22.join(CALIBER_DIR, "dismissed-checks.json");
|
|
7493
7692
|
function dismissedFilePath(dir) {
|
|
@@ -7496,17 +7695,17 @@ function dismissedFilePath(dir) {
|
|
|
7496
7695
|
function readDismissedChecks(dir) {
|
|
7497
7696
|
try {
|
|
7498
7697
|
const filePath = dismissedFilePath(dir);
|
|
7499
|
-
if (!
|
|
7500
|
-
return JSON.parse(
|
|
7698
|
+
if (!fs26.existsSync(filePath)) return [];
|
|
7699
|
+
return JSON.parse(fs26.readFileSync(filePath, "utf-8"));
|
|
7501
7700
|
} catch {
|
|
7502
7701
|
return [];
|
|
7503
7702
|
}
|
|
7504
7703
|
}
|
|
7505
7704
|
function writeDismissedChecks(checks) {
|
|
7506
|
-
if (!
|
|
7507
|
-
|
|
7705
|
+
if (!fs26.existsSync(CALIBER_DIR)) {
|
|
7706
|
+
fs26.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
7508
7707
|
}
|
|
7509
|
-
|
|
7708
|
+
fs26.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
|
|
7510
7709
|
}
|
|
7511
7710
|
function getDismissedIds(dir) {
|
|
7512
7711
|
return new Set(readDismissedChecks(dir).map((c) => c.id));
|
|
@@ -7765,7 +7964,7 @@ import { PostHog } from "posthog-node";
|
|
|
7765
7964
|
import chalk5 from "chalk";
|
|
7766
7965
|
|
|
7767
7966
|
// src/telemetry/config.ts
|
|
7768
|
-
import
|
|
7967
|
+
import fs27 from "fs";
|
|
7769
7968
|
import path23 from "path";
|
|
7770
7969
|
import os5 from "os";
|
|
7771
7970
|
import crypto4 from "crypto";
|
|
@@ -7775,17 +7974,17 @@ var CONFIG_FILE2 = path23.join(CONFIG_DIR2, "config.json");
|
|
|
7775
7974
|
var runtimeDisabled = false;
|
|
7776
7975
|
function readConfig() {
|
|
7777
7976
|
try {
|
|
7778
|
-
if (!
|
|
7779
|
-
return JSON.parse(
|
|
7977
|
+
if (!fs27.existsSync(CONFIG_FILE2)) return {};
|
|
7978
|
+
return JSON.parse(fs27.readFileSync(CONFIG_FILE2, "utf-8"));
|
|
7780
7979
|
} catch {
|
|
7781
7980
|
return {};
|
|
7782
7981
|
}
|
|
7783
7982
|
}
|
|
7784
7983
|
function writeConfig(config) {
|
|
7785
|
-
if (!
|
|
7786
|
-
|
|
7984
|
+
if (!fs27.existsSync(CONFIG_DIR2)) {
|
|
7985
|
+
fs27.mkdirSync(CONFIG_DIR2, { recursive: true });
|
|
7787
7986
|
}
|
|
7788
|
-
|
|
7987
|
+
fs27.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
7789
7988
|
}
|
|
7790
7989
|
function getMachineId() {
|
|
7791
7990
|
const config = readConfig();
|
|
@@ -8458,6 +8657,10 @@ async function recommendCommand(options) {
|
|
|
8458
8657
|
await querySkills(options.query);
|
|
8459
8658
|
return;
|
|
8460
8659
|
}
|
|
8660
|
+
if (!process.stdin.isTTY) {
|
|
8661
|
+
console.log(chalk6.dim(" Skills search requires an interactive terminal."));
|
|
8662
|
+
return;
|
|
8663
|
+
}
|
|
8461
8664
|
const proceed = await select3({
|
|
8462
8665
|
message: "Search public repos for relevant skills to add to this project?",
|
|
8463
8666
|
choices: [
|
|
@@ -9125,7 +9328,7 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory, options) {
|
|
|
9125
9328
|
}
|
|
9126
9329
|
|
|
9127
9330
|
// src/lib/debug-report.ts
|
|
9128
|
-
import
|
|
9331
|
+
import fs28 from "fs";
|
|
9129
9332
|
import path24 from "path";
|
|
9130
9333
|
var DebugReport = class {
|
|
9131
9334
|
sections = [];
|
|
@@ -9196,10 +9399,10 @@ var DebugReport = class {
|
|
|
9196
9399
|
lines.push("");
|
|
9197
9400
|
}
|
|
9198
9401
|
const dir = path24.dirname(outputPath);
|
|
9199
|
-
if (!
|
|
9200
|
-
|
|
9402
|
+
if (!fs28.existsSync(dir)) {
|
|
9403
|
+
fs28.mkdirSync(dir, { recursive: true });
|
|
9201
9404
|
}
|
|
9202
|
-
|
|
9405
|
+
fs28.writeFileSync(outputPath, lines.join("\n"));
|
|
9203
9406
|
}
|
|
9204
9407
|
};
|
|
9205
9408
|
function formatMs(ms) {
|
|
@@ -9600,7 +9803,7 @@ import chalk11 from "chalk";
|
|
|
9600
9803
|
import ora3 from "ora";
|
|
9601
9804
|
import select5 from "@inquirer/select";
|
|
9602
9805
|
import checkbox from "@inquirer/checkbox";
|
|
9603
|
-
import
|
|
9806
|
+
import fs31 from "fs";
|
|
9604
9807
|
|
|
9605
9808
|
// src/ai/refine.ts
|
|
9606
9809
|
async function refineSetup(currentSetup, message, conversationHistory, callbacks) {
|
|
@@ -9776,11 +9979,11 @@ init_config();
|
|
|
9776
9979
|
init_review();
|
|
9777
9980
|
function detectAgents(dir) {
|
|
9778
9981
|
const agents = [];
|
|
9779
|
-
if (
|
|
9780
|
-
if (
|
|
9781
|
-
if (
|
|
9782
|
-
if (
|
|
9783
|
-
if (
|
|
9982
|
+
if (fs31.existsSync(`${dir}/.claude`)) agents.push("claude");
|
|
9983
|
+
if (fs31.existsSync(`${dir}/.cursor`)) agents.push("cursor");
|
|
9984
|
+
if (fs31.existsSync(`${dir}/.agents`) || fs31.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
|
|
9985
|
+
if (fs31.existsSync(`${dir}/.opencode`)) agents.push("opencode");
|
|
9986
|
+
if (fs31.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
|
|
9784
9987
|
return agents;
|
|
9785
9988
|
}
|
|
9786
9989
|
async function promptAgent(detected) {
|
|
@@ -9884,7 +10087,7 @@ async function refineLoop(currentSetup, sessionHistory, summarizeSetup2, printSu
|
|
|
9884
10087
|
|
|
9885
10088
|
// src/commands/init-display.ts
|
|
9886
10089
|
import chalk12 from "chalk";
|
|
9887
|
-
import
|
|
10090
|
+
import fs32 from "fs";
|
|
9888
10091
|
init_types();
|
|
9889
10092
|
function formatWhatChanged(setup) {
|
|
9890
10093
|
const lines = [];
|
|
@@ -9892,12 +10095,12 @@ function formatWhatChanged(setup) {
|
|
|
9892
10095
|
const codex = setup.codex;
|
|
9893
10096
|
const cursor = setup.cursor;
|
|
9894
10097
|
if (claude?.claudeMd) {
|
|
9895
|
-
const action =
|
|
10098
|
+
const action = fs32.existsSync("CLAUDE.md") ? "Updated" : "Created";
|
|
9896
10099
|
lines.push(`${action} CLAUDE.md`);
|
|
9897
10100
|
}
|
|
9898
10101
|
const opencode = setup.opencode;
|
|
9899
10102
|
if (codex?.agentsMd || opencode?.agentsMd) {
|
|
9900
|
-
const action =
|
|
10103
|
+
const action = fs32.existsSync("AGENTS.md") ? "Updated" : "Created";
|
|
9901
10104
|
lines.push(`${action} AGENTS.md`);
|
|
9902
10105
|
}
|
|
9903
10106
|
const allSkills = [];
|
|
@@ -9941,7 +10144,7 @@ function printSetupSummary(setup) {
|
|
|
9941
10144
|
};
|
|
9942
10145
|
if (claude) {
|
|
9943
10146
|
if (claude.claudeMd) {
|
|
9944
|
-
const icon =
|
|
10147
|
+
const icon = fs32.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
|
|
9945
10148
|
const desc = getDescription("CLAUDE.md");
|
|
9946
10149
|
console.log(` ${icon} ${chalk12.bold("CLAUDE.md")}`);
|
|
9947
10150
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -9951,7 +10154,7 @@ function printSetupSummary(setup) {
|
|
|
9951
10154
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
9952
10155
|
for (const skill of skills) {
|
|
9953
10156
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
9954
|
-
const icon =
|
|
10157
|
+
const icon = fs32.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
9955
10158
|
const desc = getDescription(skillPath);
|
|
9956
10159
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
9957
10160
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -9962,7 +10165,7 @@ function printSetupSummary(setup) {
|
|
|
9962
10165
|
const codex = setup.codex;
|
|
9963
10166
|
if (codex) {
|
|
9964
10167
|
if (codex.agentsMd) {
|
|
9965
|
-
const icon =
|
|
10168
|
+
const icon = fs32.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
|
|
9966
10169
|
const desc = getDescription("AGENTS.md");
|
|
9967
10170
|
console.log(` ${icon} ${chalk12.bold("AGENTS.md")}`);
|
|
9968
10171
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -9972,7 +10175,7 @@ function printSetupSummary(setup) {
|
|
|
9972
10175
|
if (Array.isArray(codexSkills) && codexSkills.length > 0) {
|
|
9973
10176
|
for (const skill of codexSkills) {
|
|
9974
10177
|
const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
|
|
9975
|
-
const icon =
|
|
10178
|
+
const icon = fs32.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
9976
10179
|
const desc = getDescription(skillPath);
|
|
9977
10180
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
9978
10181
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -9983,7 +10186,7 @@ function printSetupSummary(setup) {
|
|
|
9983
10186
|
const opencode = setup.opencode;
|
|
9984
10187
|
if (opencode) {
|
|
9985
10188
|
if (opencode.agentsMd && !codex?.agentsMd) {
|
|
9986
|
-
const icon =
|
|
10189
|
+
const icon = fs32.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
|
|
9987
10190
|
const desc = getDescription("AGENTS.md");
|
|
9988
10191
|
console.log(` ${icon} ${chalk12.bold("AGENTS.md")} ${chalk12.dim("(OpenCode)")}`);
|
|
9989
10192
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -9993,7 +10196,7 @@ function printSetupSummary(setup) {
|
|
|
9993
10196
|
if (Array.isArray(opencodeSkills) && opencodeSkills.length > 0) {
|
|
9994
10197
|
for (const skill of opencodeSkills) {
|
|
9995
10198
|
const skillPath = `.opencode/skills/${skill.name}/SKILL.md`;
|
|
9996
|
-
const icon =
|
|
10199
|
+
const icon = fs32.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
9997
10200
|
const desc = getDescription(skillPath);
|
|
9998
10201
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
9999
10202
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -10003,7 +10206,7 @@ function printSetupSummary(setup) {
|
|
|
10003
10206
|
}
|
|
10004
10207
|
if (cursor) {
|
|
10005
10208
|
if (cursor.cursorrules) {
|
|
10006
|
-
const icon =
|
|
10209
|
+
const icon = fs32.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
|
|
10007
10210
|
const desc = getDescription(".cursorrules");
|
|
10008
10211
|
console.log(` ${icon} ${chalk12.bold(".cursorrules")}`);
|
|
10009
10212
|
if (desc) console.log(chalk12.dim(` ${desc}`));
|
|
@@ -10013,7 +10216,7 @@ function printSetupSummary(setup) {
|
|
|
10013
10216
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
10014
10217
|
for (const skill of cursorSkills) {
|
|
10015
10218
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
10016
|
-
const icon =
|
|
10219
|
+
const icon = fs32.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
10017
10220
|
const desc = getDescription(skillPath);
|
|
10018
10221
|
console.log(` ${icon} ${chalk12.bold(skillPath)}`);
|
|
10019
10222
|
console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -10024,7 +10227,7 @@ function printSetupSummary(setup) {
|
|
|
10024
10227
|
if (Array.isArray(rulesArr) && rulesArr.length > 0) {
|
|
10025
10228
|
for (const rule of rulesArr) {
|
|
10026
10229
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
10027
|
-
const icon =
|
|
10230
|
+
const icon = fs32.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
|
|
10028
10231
|
const desc = getDescription(rulePath);
|
|
10029
10232
|
console.log(` ${icon} ${chalk12.bold(rulePath)}`);
|
|
10030
10233
|
if (desc) {
|
|
@@ -10086,12 +10289,12 @@ function displayTokenUsage() {
|
|
|
10086
10289
|
// src/commands/init-helpers.ts
|
|
10087
10290
|
init_config();
|
|
10088
10291
|
import chalk13 from "chalk";
|
|
10089
|
-
import
|
|
10292
|
+
import fs33 from "fs";
|
|
10090
10293
|
import path26 from "path";
|
|
10091
10294
|
function isFirstRun(dir) {
|
|
10092
10295
|
const caliberDir = path26.join(dir, ".caliber");
|
|
10093
10296
|
try {
|
|
10094
|
-
const stat =
|
|
10297
|
+
const stat = fs33.statSync(caliberDir);
|
|
10095
10298
|
return !stat.isDirectory();
|
|
10096
10299
|
} catch {
|
|
10097
10300
|
return true;
|
|
@@ -10144,8 +10347,8 @@ function ensurePermissions(fingerprint) {
|
|
|
10144
10347
|
const settingsPath = ".claude/settings.json";
|
|
10145
10348
|
let settings = {};
|
|
10146
10349
|
try {
|
|
10147
|
-
if (
|
|
10148
|
-
settings = JSON.parse(
|
|
10350
|
+
if (fs33.existsSync(settingsPath)) {
|
|
10351
|
+
settings = JSON.parse(fs33.readFileSync(settingsPath, "utf-8"));
|
|
10149
10352
|
}
|
|
10150
10353
|
} catch {
|
|
10151
10354
|
}
|
|
@@ -10154,8 +10357,8 @@ function ensurePermissions(fingerprint) {
|
|
|
10154
10357
|
if (Array.isArray(allow) && allow.length > 0) return;
|
|
10155
10358
|
permissions.allow = derivePermissions(fingerprint);
|
|
10156
10359
|
settings.permissions = permissions;
|
|
10157
|
-
if (!
|
|
10158
|
-
|
|
10360
|
+
if (!fs33.existsSync(".claude")) fs33.mkdirSync(".claude", { recursive: true });
|
|
10361
|
+
fs33.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
10159
10362
|
}
|
|
10160
10363
|
function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
10161
10364
|
try {
|
|
@@ -10184,8 +10387,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
|
10184
10387
|
lines.push("```");
|
|
10185
10388
|
lines.push("", "If timeouts persist, try a different model.");
|
|
10186
10389
|
}
|
|
10187
|
-
|
|
10188
|
-
|
|
10390
|
+
fs33.mkdirSync(path26.join(process.cwd(), ".caliber"), { recursive: true });
|
|
10391
|
+
fs33.writeFileSync(logPath, lines.join("\n"));
|
|
10189
10392
|
console.log(chalk13.dim(`
|
|
10190
10393
|
Error log written to .caliber/error-log.md`));
|
|
10191
10394
|
} catch {
|
|
@@ -10238,7 +10441,7 @@ ${JSON.stringify(checkList, null, 2)}`,
|
|
|
10238
10441
|
}
|
|
10239
10442
|
|
|
10240
10443
|
// src/scoring/history.ts
|
|
10241
|
-
import
|
|
10444
|
+
import fs34 from "fs";
|
|
10242
10445
|
import path27 from "path";
|
|
10243
10446
|
var HISTORY_FILE = "score-history.jsonl";
|
|
10244
10447
|
var MAX_ENTRIES = 500;
|
|
@@ -10255,14 +10458,14 @@ function recordScore(result, trigger) {
|
|
|
10255
10458
|
trigger
|
|
10256
10459
|
};
|
|
10257
10460
|
try {
|
|
10258
|
-
|
|
10461
|
+
fs34.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
10259
10462
|
const filePath = historyFilePath();
|
|
10260
|
-
|
|
10261
|
-
const stat =
|
|
10463
|
+
fs34.appendFileSync(filePath, JSON.stringify(entry) + "\n");
|
|
10464
|
+
const stat = fs34.statSync(filePath);
|
|
10262
10465
|
if (stat.size > TRIM_THRESHOLD * 120) {
|
|
10263
|
-
const lines =
|
|
10466
|
+
const lines = fs34.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
10264
10467
|
if (lines.length > MAX_ENTRIES) {
|
|
10265
|
-
|
|
10468
|
+
fs34.writeFileSync(filePath, lines.slice(-MAX_ENTRIES).join("\n") + "\n");
|
|
10266
10469
|
}
|
|
10267
10470
|
}
|
|
10268
10471
|
} catch {
|
|
@@ -10271,7 +10474,7 @@ function recordScore(result, trigger) {
|
|
|
10271
10474
|
function readScoreHistory() {
|
|
10272
10475
|
const filePath = historyFilePath();
|
|
10273
10476
|
try {
|
|
10274
|
-
const content =
|
|
10477
|
+
const content = fs34.readFileSync(filePath, "utf-8");
|
|
10275
10478
|
const entries = [];
|
|
10276
10479
|
for (const line of content.split("\n")) {
|
|
10277
10480
|
if (!line) continue;
|
|
@@ -10450,12 +10653,12 @@ async function initCommand(options) {
|
|
|
10450
10653
|
}
|
|
10451
10654
|
const { ensureBuiltinSkills: ensureBuiltinSkills2 } = await Promise.resolve().then(() => (init_builtin_skills(), builtin_skills_exports));
|
|
10452
10655
|
for (const agent of targetAgent) {
|
|
10453
|
-
if (agent === "claude" && !
|
|
10454
|
-
|
|
10455
|
-
if (agent === "cursor" && !
|
|
10456
|
-
|
|
10457
|
-
if (agent === "codex" && !
|
|
10458
|
-
|
|
10656
|
+
if (agent === "claude" && !fs35.existsSync(".claude"))
|
|
10657
|
+
fs35.mkdirSync(".claude", { recursive: true });
|
|
10658
|
+
if (agent === "cursor" && !fs35.existsSync(".cursor"))
|
|
10659
|
+
fs35.mkdirSync(".cursor", { recursive: true });
|
|
10660
|
+
if (agent === "codex" && !fs35.existsSync(".agents"))
|
|
10661
|
+
fs35.mkdirSync(".agents", { recursive: true });
|
|
10459
10662
|
}
|
|
10460
10663
|
const skillsWritten = ensureBuiltinSkills2();
|
|
10461
10664
|
if (skillsWritten.length > 0) {
|
|
@@ -10544,7 +10747,7 @@ async function initCommand(options) {
|
|
|
10544
10747
|
const claudeMdPath = "CLAUDE.md";
|
|
10545
10748
|
let claudeContent = "";
|
|
10546
10749
|
try {
|
|
10547
|
-
claudeContent =
|
|
10750
|
+
claudeContent = fs35.readFileSync(claudeMdPath, "utf-8");
|
|
10548
10751
|
} catch {
|
|
10549
10752
|
}
|
|
10550
10753
|
if (!claudeContent) {
|
|
@@ -10552,20 +10755,20 @@ async function initCommand(options) {
|
|
|
10552
10755
|
`;
|
|
10553
10756
|
}
|
|
10554
10757
|
const updatedClaude = appendManagedBlocks2(claudeContent, "claude");
|
|
10555
|
-
if (updatedClaude !== claudeContent || !
|
|
10556
|
-
|
|
10758
|
+
if (updatedClaude !== claudeContent || !fs35.existsSync(claudeMdPath)) {
|
|
10759
|
+
fs35.writeFileSync(claudeMdPath, updatedClaude);
|
|
10557
10760
|
console.log(` ${chalk14.green("\u2713")} CLAUDE.md \u2014 added Caliber sync instructions`);
|
|
10558
10761
|
}
|
|
10559
10762
|
if (targetAgent.includes("cursor")) {
|
|
10560
10763
|
const rulesDir = path28.join(".cursor", "rules");
|
|
10561
|
-
if (!
|
|
10764
|
+
if (!fs35.existsSync(rulesDir)) fs35.mkdirSync(rulesDir, { recursive: true });
|
|
10562
10765
|
for (const rule of [
|
|
10563
10766
|
getCursorPreCommitRule2(),
|
|
10564
10767
|
getCursorLearningsRule2(),
|
|
10565
10768
|
getCursorSyncRule2(),
|
|
10566
10769
|
getCursorSetupRule2()
|
|
10567
10770
|
]) {
|
|
10568
|
-
|
|
10771
|
+
fs35.writeFileSync(path28.join(rulesDir, rule.filename), rule.content);
|
|
10569
10772
|
}
|
|
10570
10773
|
console.log(` ${chalk14.green("\u2713")} Cursor rules \u2014 added Caliber sync rules`);
|
|
10571
10774
|
}
|
|
@@ -10573,17 +10776,17 @@ async function initCommand(options) {
|
|
|
10573
10776
|
const copilotPath = path28.join(".github", "copilot-instructions.md");
|
|
10574
10777
|
let copilotContent = "";
|
|
10575
10778
|
try {
|
|
10576
|
-
copilotContent =
|
|
10779
|
+
copilotContent = fs35.readFileSync(copilotPath, "utf-8");
|
|
10577
10780
|
} catch {
|
|
10578
10781
|
}
|
|
10579
10782
|
if (!copilotContent) {
|
|
10580
|
-
|
|
10783
|
+
fs35.mkdirSync(".github", { recursive: true });
|
|
10581
10784
|
copilotContent = `# ${path28.basename(process.cwd())}
|
|
10582
10785
|
`;
|
|
10583
10786
|
}
|
|
10584
10787
|
const updatedCopilot = appendManagedBlocks2(copilotContent, "copilot");
|
|
10585
10788
|
if (updatedCopilot !== copilotContent) {
|
|
10586
|
-
|
|
10789
|
+
fs35.writeFileSync(copilotPath, updatedCopilot);
|
|
10587
10790
|
console.log(` ${chalk14.green("\u2713")} Copilot instructions \u2014 added Caliber sync instructions`);
|
|
10588
10791
|
}
|
|
10589
10792
|
}
|
|
@@ -10930,7 +11133,7 @@ async function initCommand(options) {
|
|
|
10930
11133
|
const { default: ora9 } = await import("ora");
|
|
10931
11134
|
const writeSpinner = ora9("Writing config files...").start();
|
|
10932
11135
|
try {
|
|
10933
|
-
if (targetAgent.includes("codex") && !
|
|
11136
|
+
if (targetAgent.includes("codex") && !fs35.existsSync("AGENTS.md") && !generatedSetup.codex) {
|
|
10934
11137
|
const claude = generatedSetup.claude;
|
|
10935
11138
|
const cursor = generatedSetup.cursor;
|
|
10936
11139
|
const agentRefs = [];
|
|
@@ -11120,7 +11323,7 @@ function undoCommand() {
|
|
|
11120
11323
|
|
|
11121
11324
|
// src/commands/status.ts
|
|
11122
11325
|
import chalk16 from "chalk";
|
|
11123
|
-
import
|
|
11326
|
+
import fs36 from "fs";
|
|
11124
11327
|
init_config();
|
|
11125
11328
|
init_resolve_caliber();
|
|
11126
11329
|
async function statusCommand(options) {
|
|
@@ -11149,7 +11352,7 @@ async function statusCommand(options) {
|
|
|
11149
11352
|
}
|
|
11150
11353
|
console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
|
|
11151
11354
|
for (const entry of manifest.entries) {
|
|
11152
|
-
const exists =
|
|
11355
|
+
const exists = fs36.existsSync(entry.path);
|
|
11153
11356
|
const icon = exists ? chalk16.green("\u2713") : chalk16.red("\u2717");
|
|
11154
11357
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
11155
11358
|
}
|
|
@@ -11306,42 +11509,42 @@ async function regenerateCommand(options) {
|
|
|
11306
11509
|
}
|
|
11307
11510
|
|
|
11308
11511
|
// src/commands/score.ts
|
|
11309
|
-
import
|
|
11512
|
+
import fs37 from "fs";
|
|
11310
11513
|
import os7 from "os";
|
|
11311
11514
|
import path29 from "path";
|
|
11312
|
-
import { execFileSync as
|
|
11515
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
11313
11516
|
import chalk18 from "chalk";
|
|
11314
11517
|
init_resolve_caliber();
|
|
11315
11518
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS.md"];
|
|
11316
11519
|
var CONFIG_DIRS = [".claude", ".cursor"];
|
|
11317
11520
|
function scoreBaseRef(ref, target) {
|
|
11318
11521
|
if (!/^[\w.\-/~^@{}]+$/.test(ref)) return null;
|
|
11319
|
-
const tmpDir =
|
|
11522
|
+
const tmpDir = fs37.mkdtempSync(path29.join(os7.tmpdir(), "caliber-compare-"));
|
|
11320
11523
|
try {
|
|
11321
11524
|
for (const file of CONFIG_FILES) {
|
|
11322
11525
|
try {
|
|
11323
|
-
const content =
|
|
11526
|
+
const content = execFileSync4("git", ["show", `${ref}:${file}`], {
|
|
11324
11527
|
encoding: "utf-8",
|
|
11325
11528
|
stdio: ["pipe", "pipe", "pipe"]
|
|
11326
11529
|
});
|
|
11327
|
-
|
|
11530
|
+
fs37.writeFileSync(path29.join(tmpDir, file), content);
|
|
11328
11531
|
} catch {
|
|
11329
11532
|
}
|
|
11330
11533
|
}
|
|
11331
11534
|
for (const dir of CONFIG_DIRS) {
|
|
11332
11535
|
try {
|
|
11333
|
-
const files =
|
|
11536
|
+
const files = execFileSync4("git", ["ls-tree", "-r", "--name-only", ref, `${dir}/`], {
|
|
11334
11537
|
encoding: "utf-8",
|
|
11335
11538
|
stdio: ["pipe", "pipe", "pipe"]
|
|
11336
11539
|
}).trim().split("\n").filter(Boolean);
|
|
11337
11540
|
for (const file of files) {
|
|
11338
11541
|
const filePath = path29.join(tmpDir, file);
|
|
11339
|
-
|
|
11340
|
-
const content =
|
|
11542
|
+
fs37.mkdirSync(path29.dirname(filePath), { recursive: true });
|
|
11543
|
+
const content = execFileSync4("git", ["show", `${ref}:${file}`], {
|
|
11341
11544
|
encoding: "utf-8",
|
|
11342
11545
|
stdio: ["pipe", "pipe", "pipe"]
|
|
11343
11546
|
});
|
|
11344
|
-
|
|
11547
|
+
fs37.writeFileSync(filePath, content);
|
|
11345
11548
|
}
|
|
11346
11549
|
} catch {
|
|
11347
11550
|
}
|
|
@@ -11351,7 +11554,7 @@ function scoreBaseRef(ref, target) {
|
|
|
11351
11554
|
} catch {
|
|
11352
11555
|
return null;
|
|
11353
11556
|
} finally {
|
|
11354
|
-
|
|
11557
|
+
fs37.rmSync(tmpDir, { recursive: true, force: true });
|
|
11355
11558
|
}
|
|
11356
11559
|
}
|
|
11357
11560
|
async function scoreCommand(options) {
|
|
@@ -11444,10 +11647,11 @@ async function scoreCommand(options) {
|
|
|
11444
11647
|
}
|
|
11445
11648
|
|
|
11446
11649
|
// src/commands/refresh.ts
|
|
11447
|
-
import
|
|
11650
|
+
import fs42 from "fs";
|
|
11448
11651
|
import path34 from "path";
|
|
11449
11652
|
import chalk19 from "chalk";
|
|
11450
11653
|
import ora6 from "ora";
|
|
11654
|
+
import pLimit from "p-limit";
|
|
11451
11655
|
|
|
11452
11656
|
// src/lib/git-diff.ts
|
|
11453
11657
|
import { execSync as execSync15 } from "child_process";
|
|
@@ -11560,13 +11764,13 @@ function scopeDiffToDir(diff, dir, allConfigDirs) {
|
|
|
11560
11764
|
|
|
11561
11765
|
// src/writers/refresh.ts
|
|
11562
11766
|
init_pre_commit_block();
|
|
11563
|
-
import
|
|
11767
|
+
import fs38 from "fs";
|
|
11564
11768
|
import path30 from "path";
|
|
11565
11769
|
function writeFileGroup(groupDir, files) {
|
|
11566
|
-
|
|
11770
|
+
fs38.mkdirSync(groupDir, { recursive: true });
|
|
11567
11771
|
return files.map((file) => {
|
|
11568
11772
|
const filePath = path30.join(groupDir, file.filename);
|
|
11569
|
-
|
|
11773
|
+
fs38.writeFileSync(filePath, file.content);
|
|
11570
11774
|
return filePath.replace(/\\/g, "/");
|
|
11571
11775
|
});
|
|
11572
11776
|
}
|
|
@@ -11575,18 +11779,18 @@ function writeRefreshDocs(docs, dir = ".") {
|
|
|
11575
11779
|
const p = (relPath) => (dir === "." ? relPath : path30.join(dir, relPath)).replace(/\\/g, "/");
|
|
11576
11780
|
const ensureParent = (filePath) => {
|
|
11577
11781
|
const parent = path30.dirname(filePath);
|
|
11578
|
-
if (parent !== "." && !
|
|
11782
|
+
if (parent !== "." && !fs38.existsSync(parent)) fs38.mkdirSync(parent, { recursive: true });
|
|
11579
11783
|
};
|
|
11580
11784
|
if (docs.agentsMd) {
|
|
11581
11785
|
const filePath = p("AGENTS.md");
|
|
11582
11786
|
ensureParent(filePath);
|
|
11583
|
-
|
|
11787
|
+
fs38.writeFileSync(filePath, appendManagedBlocks(docs.agentsMd, "codex"));
|
|
11584
11788
|
written.push(filePath);
|
|
11585
11789
|
}
|
|
11586
11790
|
if (docs.claudeMd) {
|
|
11587
11791
|
const filePath = p("CLAUDE.md");
|
|
11588
11792
|
ensureParent(filePath);
|
|
11589
|
-
|
|
11793
|
+
fs38.writeFileSync(filePath, appendManagedBlocks(docs.claudeMd));
|
|
11590
11794
|
written.push(filePath);
|
|
11591
11795
|
}
|
|
11592
11796
|
if (docs.claudeRules) {
|
|
@@ -11595,13 +11799,13 @@ function writeRefreshDocs(docs, dir = ".") {
|
|
|
11595
11799
|
if (docs.readmeMd) {
|
|
11596
11800
|
const filePath = p("README.md");
|
|
11597
11801
|
ensureParent(filePath);
|
|
11598
|
-
|
|
11802
|
+
fs38.writeFileSync(filePath, docs.readmeMd);
|
|
11599
11803
|
written.push(filePath);
|
|
11600
11804
|
}
|
|
11601
11805
|
if (docs.cursorrules) {
|
|
11602
11806
|
const filePath = p(".cursorrules");
|
|
11603
11807
|
ensureParent(filePath);
|
|
11604
|
-
|
|
11808
|
+
fs38.writeFileSync(filePath, docs.cursorrules);
|
|
11605
11809
|
written.push(filePath);
|
|
11606
11810
|
}
|
|
11607
11811
|
if (docs.cursorRules) {
|
|
@@ -11610,7 +11814,7 @@ function writeRefreshDocs(docs, dir = ".") {
|
|
|
11610
11814
|
if (docs.copilotInstructions) {
|
|
11611
11815
|
const filePath = p(path30.join(".github", "copilot-instructions.md"));
|
|
11612
11816
|
ensureParent(filePath);
|
|
11613
|
-
|
|
11817
|
+
fs38.writeFileSync(filePath, appendManagedBlocks(docs.copilotInstructions, "copilot"));
|
|
11614
11818
|
written.push(filePath);
|
|
11615
11819
|
}
|
|
11616
11820
|
if (docs.copilotInstructionFiles) {
|
|
@@ -11752,7 +11956,7 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
11752
11956
|
}
|
|
11753
11957
|
|
|
11754
11958
|
// src/learner/writer.ts
|
|
11755
|
-
import
|
|
11959
|
+
import fs39 from "fs";
|
|
11756
11960
|
import path31 from "path";
|
|
11757
11961
|
|
|
11758
11962
|
// src/learner/utils.ts
|
|
@@ -11870,8 +12074,8 @@ function deduplicateLearnedItems(existing, incoming) {
|
|
|
11870
12074
|
}
|
|
11871
12075
|
function writeLearnedSectionTo(filePath, header, existing, incoming, mode) {
|
|
11872
12076
|
const { merged, newCount, newItems } = deduplicateLearnedItems(existing, incoming);
|
|
11873
|
-
|
|
11874
|
-
if (mode)
|
|
12077
|
+
fs39.writeFileSync(filePath, header + merged + "\n");
|
|
12078
|
+
if (mode) fs39.chmodSync(filePath, mode);
|
|
11875
12079
|
return { newCount, newItems };
|
|
11876
12080
|
}
|
|
11877
12081
|
function writeLearnedSection(content) {
|
|
@@ -11879,11 +12083,11 @@ function writeLearnedSection(content) {
|
|
|
11879
12083
|
}
|
|
11880
12084
|
function writeLearnedSkill(skill) {
|
|
11881
12085
|
const skillDir = path31.join(".claude", "skills", skill.name);
|
|
11882
|
-
if (!
|
|
12086
|
+
if (!fs39.existsSync(skillDir)) fs39.mkdirSync(skillDir, { recursive: true });
|
|
11883
12087
|
const skillPath = path31.join(skillDir, "SKILL.md");
|
|
11884
|
-
if (!skill.isNew &&
|
|
11885
|
-
const existing =
|
|
11886
|
-
|
|
12088
|
+
if (!skill.isNew && fs39.existsSync(skillPath)) {
|
|
12089
|
+
const existing = fs39.readFileSync(skillPath, "utf-8");
|
|
12090
|
+
fs39.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
11887
12091
|
} else {
|
|
11888
12092
|
const frontmatter = [
|
|
11889
12093
|
"---",
|
|
@@ -11892,12 +12096,12 @@ function writeLearnedSkill(skill) {
|
|
|
11892
12096
|
"---",
|
|
11893
12097
|
""
|
|
11894
12098
|
].join("\n");
|
|
11895
|
-
|
|
12099
|
+
fs39.writeFileSync(skillPath, frontmatter + skill.content);
|
|
11896
12100
|
}
|
|
11897
12101
|
return skillPath;
|
|
11898
12102
|
}
|
|
11899
12103
|
function writePersonalLearnedSection(content) {
|
|
11900
|
-
if (!
|
|
12104
|
+
if (!fs39.existsSync(AUTH_DIR)) fs39.mkdirSync(AUTH_DIR, { recursive: true });
|
|
11901
12105
|
return writeLearnedSectionTo(PERSONAL_LEARNINGS_FILE, PERSONAL_LEARNINGS_HEADER, readPersonalLearnings(), content, 384);
|
|
11902
12106
|
}
|
|
11903
12107
|
function addLearning(bullet, scope = "project") {
|
|
@@ -11910,38 +12114,38 @@ function addLearning(bullet, scope = "project") {
|
|
|
11910
12114
|
return { file: LEARNINGS_FILE, added: result.newCount > 0 };
|
|
11911
12115
|
}
|
|
11912
12116
|
function readPersonalLearnings() {
|
|
11913
|
-
if (!
|
|
11914
|
-
const content =
|
|
12117
|
+
if (!fs39.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
|
|
12118
|
+
const content = fs39.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
|
|
11915
12119
|
const bullets = content.split("\n").filter((l) => l.startsWith("- ")).join("\n");
|
|
11916
12120
|
return bullets || null;
|
|
11917
12121
|
}
|
|
11918
12122
|
function readLearnedSection() {
|
|
11919
|
-
if (
|
|
11920
|
-
const content2 =
|
|
12123
|
+
if (fs39.existsSync(LEARNINGS_FILE)) {
|
|
12124
|
+
const content2 = fs39.readFileSync(LEARNINGS_FILE, "utf-8");
|
|
11921
12125
|
const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
|
|
11922
12126
|
return bullets || null;
|
|
11923
12127
|
}
|
|
11924
12128
|
const claudeMdPath = "CLAUDE.md";
|
|
11925
|
-
if (!
|
|
11926
|
-
const content =
|
|
12129
|
+
if (!fs39.existsSync(claudeMdPath)) return null;
|
|
12130
|
+
const content = fs39.readFileSync(claudeMdPath, "utf-8");
|
|
11927
12131
|
const startIdx = content.indexOf(LEARNED_START);
|
|
11928
12132
|
const endIdx = content.indexOf(LEARNED_END);
|
|
11929
12133
|
if (startIdx === -1 || endIdx === -1) return null;
|
|
11930
12134
|
return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
|
|
11931
12135
|
}
|
|
11932
12136
|
function migrateInlineLearnings() {
|
|
11933
|
-
if (
|
|
12137
|
+
if (fs39.existsSync(LEARNINGS_FILE)) return false;
|
|
11934
12138
|
const claudeMdPath = "CLAUDE.md";
|
|
11935
|
-
if (!
|
|
11936
|
-
const content =
|
|
12139
|
+
if (!fs39.existsSync(claudeMdPath)) return false;
|
|
12140
|
+
const content = fs39.readFileSync(claudeMdPath, "utf-8");
|
|
11937
12141
|
const startIdx = content.indexOf(LEARNED_START);
|
|
11938
12142
|
const endIdx = content.indexOf(LEARNED_END);
|
|
11939
12143
|
if (startIdx === -1 || endIdx === -1) return false;
|
|
11940
12144
|
const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
|
|
11941
12145
|
if (!section) return false;
|
|
11942
|
-
|
|
12146
|
+
fs39.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
|
|
11943
12147
|
const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
|
|
11944
|
-
|
|
12148
|
+
fs39.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
|
|
11945
12149
|
return true;
|
|
11946
12150
|
}
|
|
11947
12151
|
|
|
@@ -11951,7 +12155,7 @@ init_resolve_caliber();
|
|
|
11951
12155
|
init_builtin_skills();
|
|
11952
12156
|
|
|
11953
12157
|
// src/lib/config-discovery.ts
|
|
11954
|
-
import
|
|
12158
|
+
import fs40 from "fs";
|
|
11955
12159
|
import path32 from "path";
|
|
11956
12160
|
var CONFIG_FILE_MARKERS = [
|
|
11957
12161
|
"CLAUDE.md",
|
|
@@ -11978,11 +12182,11 @@ var IGNORE_DIRS3 = /* @__PURE__ */ new Set([
|
|
|
11978
12182
|
var MAX_DEPTH = 4;
|
|
11979
12183
|
function hasConfigFiles(dir) {
|
|
11980
12184
|
for (const marker of CONFIG_FILE_MARKERS) {
|
|
11981
|
-
if (
|
|
12185
|
+
if (fs40.existsSync(path32.join(dir, marker))) return true;
|
|
11982
12186
|
}
|
|
11983
12187
|
for (const marker of CONFIG_DIR_MARKERS) {
|
|
11984
12188
|
const markerPath = path32.join(dir, marker);
|
|
11985
|
-
if (
|
|
12189
|
+
if (fs40.existsSync(markerPath) && fs40.statSync(markerPath).isDirectory()) return true;
|
|
11986
12190
|
}
|
|
11987
12191
|
return false;
|
|
11988
12192
|
}
|
|
@@ -11999,7 +12203,7 @@ function walkForConfigs(baseDir, currentDir, depth, result) {
|
|
|
11999
12203
|
if (depth >= MAX_DEPTH) return;
|
|
12000
12204
|
let entries;
|
|
12001
12205
|
try {
|
|
12002
|
-
entries =
|
|
12206
|
+
entries = fs40.readdirSync(currentDir, { withFileTypes: true });
|
|
12003
12207
|
} catch {
|
|
12004
12208
|
return;
|
|
12005
12209
|
}
|
|
@@ -12018,8 +12222,8 @@ function walkForConfigs(baseDir, currentDir, depth, result) {
|
|
|
12018
12222
|
// src/commands/refresh.ts
|
|
12019
12223
|
function writeRefreshError(error) {
|
|
12020
12224
|
try {
|
|
12021
|
-
if (!
|
|
12022
|
-
|
|
12225
|
+
if (!fs42.existsSync(CALIBER_DIR)) fs42.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
12226
|
+
fs42.writeFileSync(
|
|
12023
12227
|
REFRESH_LAST_ERROR_FILE,
|
|
12024
12228
|
JSON.stringify(
|
|
12025
12229
|
{
|
|
@@ -12038,15 +12242,15 @@ function writeRefreshError(error) {
|
|
|
12038
12242
|
}
|
|
12039
12243
|
function readRefreshError() {
|
|
12040
12244
|
try {
|
|
12041
|
-
if (!
|
|
12042
|
-
return JSON.parse(
|
|
12245
|
+
if (!fs42.existsSync(REFRESH_LAST_ERROR_FILE)) return null;
|
|
12246
|
+
return JSON.parse(fs42.readFileSync(REFRESH_LAST_ERROR_FILE, "utf-8"));
|
|
12043
12247
|
} catch {
|
|
12044
12248
|
return null;
|
|
12045
12249
|
}
|
|
12046
12250
|
}
|
|
12047
12251
|
function clearRefreshError() {
|
|
12048
12252
|
try {
|
|
12049
|
-
if (
|
|
12253
|
+
if (fs42.existsSync(REFRESH_LAST_ERROR_FILE)) fs42.unlinkSync(REFRESH_LAST_ERROR_FILE);
|
|
12050
12254
|
} catch {
|
|
12051
12255
|
}
|
|
12052
12256
|
}
|
|
@@ -12066,11 +12270,11 @@ function log2(quiet, ...args) {
|
|
|
12066
12270
|
function discoverGitRepos(parentDir) {
|
|
12067
12271
|
const repos = [];
|
|
12068
12272
|
try {
|
|
12069
|
-
const entries =
|
|
12273
|
+
const entries = fs42.readdirSync(parentDir, { withFileTypes: true });
|
|
12070
12274
|
for (const entry of entries) {
|
|
12071
12275
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
12072
12276
|
const childPath = path34.join(parentDir, entry.name);
|
|
12073
|
-
if (
|
|
12277
|
+
if (fs42.existsSync(path34.join(childPath, ".git"))) {
|
|
12074
12278
|
repos.push(childPath);
|
|
12075
12279
|
}
|
|
12076
12280
|
}
|
|
@@ -12101,12 +12305,15 @@ function collectFilesToWrite(updatedDocs, dir = ".") {
|
|
|
12101
12305
|
return files;
|
|
12102
12306
|
}
|
|
12103
12307
|
var REFRESH_COOLDOWN_MS = 3e4;
|
|
12308
|
+
var PARALLEL_DIR_CONCURRENCY = 4;
|
|
12104
12309
|
async function refreshDir(repoDir, dir, diff, options) {
|
|
12105
12310
|
const quiet = !!options.quiet;
|
|
12311
|
+
const suppress = !!options.suppressSpinner;
|
|
12312
|
+
const effectiveQuiet = quiet || suppress;
|
|
12106
12313
|
const prefix = options.label ? `${chalk19.bold(options.label)} ` : "";
|
|
12107
12314
|
const absDir = dir === "." ? repoDir : path34.resolve(repoDir, dir);
|
|
12108
12315
|
const scope = dir === "." ? void 0 : dir;
|
|
12109
|
-
const spinner =
|
|
12316
|
+
const spinner = effectiveQuiet ? null : ora6(`${prefix}Analyzing changes...`).start();
|
|
12110
12317
|
const learnedSection = readLearnedSection();
|
|
12111
12318
|
const fingerprint = await collectFingerprint(absDir);
|
|
12112
12319
|
const existingDocs = fingerprint.existingConfigs;
|
|
@@ -12155,7 +12362,7 @@ async function refreshDir(repoDir, dir, diff, options) {
|
|
|
12155
12362
|
}
|
|
12156
12363
|
if (!response.docsUpdated || response.docsUpdated.length === 0) {
|
|
12157
12364
|
spinner?.succeed(`${prefix}No doc updates needed`);
|
|
12158
|
-
return { written: [] };
|
|
12365
|
+
return { written: [], fileChanges: [], syncedAgents: [], changesSummary: null };
|
|
12159
12366
|
}
|
|
12160
12367
|
if (options.dryRun) {
|
|
12161
12368
|
spinner?.info(`${prefix}Dry run \u2014 would update:`);
|
|
@@ -12166,14 +12373,14 @@ async function refreshDir(repoDir, dir, diff, options) {
|
|
|
12166
12373
|
console.log(chalk19.dim(`
|
|
12167
12374
|
${response.changesSummary}`));
|
|
12168
12375
|
}
|
|
12169
|
-
return { written: [] };
|
|
12376
|
+
return { written: [], fileChanges: [], syncedAgents: [], changesSummary: null };
|
|
12170
12377
|
}
|
|
12171
12378
|
const allFilesToWrite = collectFilesToWrite(response.updatedDocs, dir);
|
|
12172
12379
|
const preRefreshContents = /* @__PURE__ */ new Map();
|
|
12173
12380
|
for (const filePath of allFilesToWrite) {
|
|
12174
12381
|
const fullPath = path34.resolve(repoDir, filePath);
|
|
12175
12382
|
try {
|
|
12176
|
-
preRefreshContents.set(filePath,
|
|
12383
|
+
preRefreshContents.set(filePath, fs42.readFileSync(fullPath, "utf-8"));
|
|
12177
12384
|
} catch {
|
|
12178
12385
|
preRefreshContents.set(filePath, null);
|
|
12179
12386
|
}
|
|
@@ -12192,43 +12399,47 @@ async function refreshDir(repoDir, dir, diff, options) {
|
|
|
12192
12399
|
const fullPath = path34.resolve(repoDir, filePath);
|
|
12193
12400
|
if (content === null) {
|
|
12194
12401
|
try {
|
|
12195
|
-
|
|
12402
|
+
fs42.unlinkSync(fullPath);
|
|
12196
12403
|
} catch {
|
|
12197
12404
|
}
|
|
12198
12405
|
} else {
|
|
12199
|
-
|
|
12406
|
+
fs42.writeFileSync(fullPath, content);
|
|
12200
12407
|
}
|
|
12201
12408
|
}
|
|
12202
12409
|
spinner?.warn(
|
|
12203
12410
|
`${prefix}Refresh reverted \u2014 score would drop from ${preScore.score} to ${postScore.score}`
|
|
12204
12411
|
);
|
|
12205
|
-
log2(
|
|
12206
|
-
|
|
12412
|
+
log2(
|
|
12413
|
+
effectiveQuiet,
|
|
12414
|
+
chalk19.dim(` Config quality gate prevented a regression. No files were changed.`)
|
|
12415
|
+
);
|
|
12416
|
+
return { written: [], fileChanges: [], syncedAgents: [], changesSummary: null };
|
|
12207
12417
|
}
|
|
12208
12418
|
recordScore(postScore, "refresh");
|
|
12209
12419
|
}
|
|
12210
12420
|
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
|
-
|
|
12421
|
+
const fileChanges = response.fileChanges || [];
|
|
12422
|
+
const fileChangesMap = new Map(fileChanges.map((fc) => [fc.file, fc.description]));
|
|
12423
|
+
const syncedAgents = detectSyncedAgents(written);
|
|
12424
|
+
if (!suppress) {
|
|
12425
|
+
for (const file of written) {
|
|
12426
|
+
const desc = fileChangesMap.get(file);
|
|
12427
|
+
const suffix = desc ? chalk19.dim(` \u2014 ${desc}`) : "";
|
|
12428
|
+
log2(effectiveQuiet, ` ${chalk19.green("\u2713")} ${file}${suffix}`);
|
|
12429
|
+
}
|
|
12430
|
+
if (syncedAgents.length > 1) {
|
|
12431
|
+
log2(
|
|
12432
|
+
effectiveQuiet,
|
|
12433
|
+
chalk19.cyan(`
|
|
12434
|
+
${syncedAgents.length} agent formats in sync (${syncedAgents.join(", ")})`)
|
|
12435
|
+
);
|
|
12436
|
+
}
|
|
12437
|
+
if (response.changesSummary) {
|
|
12438
|
+
log2(effectiveQuiet, chalk19.dim(`
|
|
12229
12439
|
${response.changesSummary}`));
|
|
12440
|
+
}
|
|
12230
12441
|
}
|
|
12231
|
-
return { written };
|
|
12442
|
+
return { written, fileChanges, syncedAgents, changesSummary: response.changesSummary };
|
|
12232
12443
|
}
|
|
12233
12444
|
async function refreshSingleRepo(repoDir, options) {
|
|
12234
12445
|
const quiet = !!options.quiet;
|
|
@@ -12260,21 +12471,57 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
12260
12471
|
} else {
|
|
12261
12472
|
log2(quiet, chalk19.dim(`${prefix}Found configs in ${configDirs.length} directories
|
|
12262
12473
|
`));
|
|
12474
|
+
const dirsWithChanges = configDirs.map((dir) => ({ dir, scopedDiff: scopeDiffToDir(diff, dir, configDirs) })).filter(({ scopedDiff }) => scopedDiff.hasChanges);
|
|
12475
|
+
const parallelSpinner = quiet ? null : ora6(
|
|
12476
|
+
`${prefix}Refreshing ${dirsWithChanges.length} director${dirsWithChanges.length === 1 ? "y" : "ies"}...`
|
|
12477
|
+
).start();
|
|
12478
|
+
const limit = pLimit(PARALLEL_DIR_CONCURRENCY);
|
|
12479
|
+
const results = await Promise.allSettled(
|
|
12480
|
+
dirsWithChanges.map(({ dir, scopedDiff }) => {
|
|
12481
|
+
const dirLabel = dir === "." ? "root" : dir;
|
|
12482
|
+
return limit(
|
|
12483
|
+
() => refreshDir(repoDir, dir, scopedDiff, {
|
|
12484
|
+
...options,
|
|
12485
|
+
suppressSpinner: true,
|
|
12486
|
+
label: dirLabel
|
|
12487
|
+
})
|
|
12488
|
+
);
|
|
12489
|
+
})
|
|
12490
|
+
);
|
|
12491
|
+
parallelSpinner?.stop();
|
|
12263
12492
|
let hadFailure = false;
|
|
12264
|
-
for (const
|
|
12265
|
-
const
|
|
12266
|
-
if (!scopedDiff.hasChanges) continue;
|
|
12493
|
+
for (const [i, result] of results.entries()) {
|
|
12494
|
+
const { dir } = dirsWithChanges[i];
|
|
12267
12495
|
const dirLabel = dir === "." ? "root" : dir;
|
|
12268
|
-
|
|
12269
|
-
await refreshDir(repoDir, dir, scopedDiff, { ...options, label: dirLabel });
|
|
12270
|
-
} catch (err) {
|
|
12496
|
+
if (result.status === "rejected") {
|
|
12271
12497
|
hadFailure = true;
|
|
12272
12498
|
log2(
|
|
12273
12499
|
quiet,
|
|
12274
12500
|
chalk19.yellow(
|
|
12275
|
-
` ${dirLabel}: refresh failed \u2014 ${
|
|
12501
|
+
` ${dirLabel}: refresh failed \u2014 ${result.reason instanceof Error ? result.reason.message : "unknown error"}`
|
|
12276
12502
|
)
|
|
12277
12503
|
);
|
|
12504
|
+
} else {
|
|
12505
|
+
const { written, fileChanges, syncedAgents, changesSummary } = result.value;
|
|
12506
|
+
const fileChangesMap = new Map(fileChanges.map((fc) => [fc.file, fc.description]));
|
|
12507
|
+
for (const file of written) {
|
|
12508
|
+
const desc = fileChangesMap.get(file);
|
|
12509
|
+
const suffix = desc ? chalk19.dim(` \u2014 ${desc}`) : "";
|
|
12510
|
+
log2(quiet, ` ${chalk19.green("\u2713")} ${dirLabel}/${file}${suffix}`);
|
|
12511
|
+
}
|
|
12512
|
+
if (syncedAgents.length > 1) {
|
|
12513
|
+
log2(
|
|
12514
|
+
quiet,
|
|
12515
|
+
chalk19.cyan(
|
|
12516
|
+
`
|
|
12517
|
+
${syncedAgents.length} agent formats in sync (${syncedAgents.join(", ")})`
|
|
12518
|
+
)
|
|
12519
|
+
);
|
|
12520
|
+
}
|
|
12521
|
+
if (changesSummary) {
|
|
12522
|
+
log2(quiet, chalk19.dim(`
|
|
12523
|
+
${changesSummary}`));
|
|
12524
|
+
}
|
|
12278
12525
|
}
|
|
12279
12526
|
}
|
|
12280
12527
|
if (hadFailure) {
|
|
@@ -12366,7 +12613,7 @@ async function refreshCommand(options) {
|
|
|
12366
12613
|
|
|
12367
12614
|
// src/commands/hooks.ts
|
|
12368
12615
|
import chalk20 from "chalk";
|
|
12369
|
-
import
|
|
12616
|
+
import fs43 from "fs";
|
|
12370
12617
|
var HOOKS = [
|
|
12371
12618
|
{
|
|
12372
12619
|
id: "session-end",
|
|
@@ -12430,11 +12677,11 @@ async function hooksCommand(options) {
|
|
|
12430
12677
|
console.log(chalk20.green(" \u2713") + ` ${hook.label} enabled`);
|
|
12431
12678
|
}
|
|
12432
12679
|
}
|
|
12433
|
-
if (
|
|
12680
|
+
if (fs43.existsSync(".claude")) {
|
|
12434
12681
|
const r = installLearningHooks();
|
|
12435
12682
|
if (r.installed) console.log(chalk20.green(" \u2713") + " Claude Code learning hooks enabled");
|
|
12436
12683
|
}
|
|
12437
|
-
if (
|
|
12684
|
+
if (fs43.existsSync(".cursor")) {
|
|
12438
12685
|
const r = installCursorLearningHooks();
|
|
12439
12686
|
if (r.installed) console.log(chalk20.green(" \u2713") + " Cursor learning hooks enabled");
|
|
12440
12687
|
}
|
|
@@ -12602,7 +12849,7 @@ async function configCommand() {
|
|
|
12602
12849
|
}
|
|
12603
12850
|
|
|
12604
12851
|
// src/commands/learn.ts
|
|
12605
|
-
import
|
|
12852
|
+
import fs47 from "fs";
|
|
12606
12853
|
import path38 from "path";
|
|
12607
12854
|
import chalk23 from "chalk";
|
|
12608
12855
|
|
|
@@ -12634,7 +12881,7 @@ function readStdin() {
|
|
|
12634
12881
|
}
|
|
12635
12882
|
|
|
12636
12883
|
// src/learner/storage.ts
|
|
12637
|
-
import
|
|
12884
|
+
import fs44 from "fs";
|
|
12638
12885
|
import path35 from "path";
|
|
12639
12886
|
var MAX_RESPONSE_LENGTH = 2e3;
|
|
12640
12887
|
var MAX_PROMPT_LENGTH = 2e3;
|
|
@@ -12646,8 +12893,8 @@ var DEFAULT_STATE = {
|
|
|
12646
12893
|
lastAnalysisEventCount: 0
|
|
12647
12894
|
};
|
|
12648
12895
|
function ensureLearningDir() {
|
|
12649
|
-
if (!
|
|
12650
|
-
|
|
12896
|
+
if (!fs44.existsSync(getLearningDir())) {
|
|
12897
|
+
fs44.mkdirSync(getLearningDir(), { recursive: true });
|
|
12651
12898
|
}
|
|
12652
12899
|
}
|
|
12653
12900
|
function sessionFilePath() {
|
|
@@ -12663,9 +12910,9 @@ function truncateResponse(response) {
|
|
|
12663
12910
|
}
|
|
12664
12911
|
function trimSessionFileIfNeeded(filePath) {
|
|
12665
12912
|
try {
|
|
12666
|
-
const stat =
|
|
12913
|
+
const stat = fs44.statSync(filePath);
|
|
12667
12914
|
if (stat.size > MAX_SESSION_FILE_BYTES) {
|
|
12668
|
-
|
|
12915
|
+
fs44.writeFileSync(filePath, "");
|
|
12669
12916
|
resetState();
|
|
12670
12917
|
return;
|
|
12671
12918
|
}
|
|
@@ -12674,10 +12921,10 @@ function trimSessionFileIfNeeded(filePath) {
|
|
|
12674
12921
|
}
|
|
12675
12922
|
const state = readState2();
|
|
12676
12923
|
if (state.eventCount + 1 > LEARNING_MAX_EVENTS) {
|
|
12677
|
-
const lines =
|
|
12924
|
+
const lines = fs44.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
12678
12925
|
if (lines.length > LEARNING_MAX_EVENTS) {
|
|
12679
12926
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
12680
|
-
|
|
12927
|
+
fs44.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
12681
12928
|
}
|
|
12682
12929
|
}
|
|
12683
12930
|
}
|
|
@@ -12685,7 +12932,7 @@ function appendEvent(event) {
|
|
|
12685
12932
|
ensureLearningDir();
|
|
12686
12933
|
const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
|
|
12687
12934
|
const filePath = sessionFilePath();
|
|
12688
|
-
|
|
12935
|
+
fs44.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
12689
12936
|
trimSessionFileIfNeeded(filePath);
|
|
12690
12937
|
}
|
|
12691
12938
|
function appendPromptEvent(event) {
|
|
@@ -12695,22 +12942,22 @@ function appendPromptEvent(event) {
|
|
|
12695
12942
|
prompt_content: event.prompt_content.length > MAX_PROMPT_LENGTH ? event.prompt_content.slice(0, MAX_PROMPT_LENGTH) : event.prompt_content
|
|
12696
12943
|
};
|
|
12697
12944
|
const filePath = sessionFilePath();
|
|
12698
|
-
|
|
12945
|
+
fs44.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
12699
12946
|
trimSessionFileIfNeeded(filePath);
|
|
12700
12947
|
}
|
|
12701
12948
|
function readAllEvents() {
|
|
12702
12949
|
const filePath = sessionFilePath();
|
|
12703
12950
|
try {
|
|
12704
|
-
const stat =
|
|
12951
|
+
const stat = fs44.statSync(filePath);
|
|
12705
12952
|
if (stat.size > MAX_SESSION_FILE_BYTES) {
|
|
12706
|
-
|
|
12953
|
+
fs44.writeFileSync(filePath, "");
|
|
12707
12954
|
resetState();
|
|
12708
12955
|
return [];
|
|
12709
12956
|
}
|
|
12710
12957
|
} catch {
|
|
12711
12958
|
return [];
|
|
12712
12959
|
}
|
|
12713
|
-
const lines =
|
|
12960
|
+
const lines = fs44.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
12714
12961
|
const events = [];
|
|
12715
12962
|
for (const line of lines) {
|
|
12716
12963
|
try {
|
|
@@ -12723,33 +12970,33 @@ function readAllEvents() {
|
|
|
12723
12970
|
function getEventCount() {
|
|
12724
12971
|
const filePath = sessionFilePath();
|
|
12725
12972
|
try {
|
|
12726
|
-
const stat =
|
|
12973
|
+
const stat = fs44.statSync(filePath);
|
|
12727
12974
|
if (stat.size > MAX_SESSION_FILE_BYTES) return 0;
|
|
12728
12975
|
} catch {
|
|
12729
12976
|
return 0;
|
|
12730
12977
|
}
|
|
12731
|
-
const content =
|
|
12978
|
+
const content = fs44.readFileSync(filePath, "utf-8");
|
|
12732
12979
|
return content.split("\n").filter(Boolean).length;
|
|
12733
12980
|
}
|
|
12734
12981
|
function clearSession() {
|
|
12735
12982
|
const filePath = sessionFilePath();
|
|
12736
12983
|
try {
|
|
12737
|
-
|
|
12984
|
+
fs44.writeFileSync(filePath, "");
|
|
12738
12985
|
} catch {
|
|
12739
12986
|
}
|
|
12740
12987
|
}
|
|
12741
12988
|
function readState2() {
|
|
12742
12989
|
const filePath = stateFilePath();
|
|
12743
|
-
if (!
|
|
12990
|
+
if (!fs44.existsSync(filePath)) return { ...DEFAULT_STATE };
|
|
12744
12991
|
try {
|
|
12745
|
-
return JSON.parse(
|
|
12992
|
+
return JSON.parse(fs44.readFileSync(filePath, "utf-8"));
|
|
12746
12993
|
} catch {
|
|
12747
12994
|
return { ...DEFAULT_STATE };
|
|
12748
12995
|
}
|
|
12749
12996
|
}
|
|
12750
12997
|
function writeState2(state) {
|
|
12751
12998
|
ensureLearningDir();
|
|
12752
|
-
|
|
12999
|
+
fs44.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
|
|
12753
13000
|
}
|
|
12754
13001
|
function resetState() {
|
|
12755
13002
|
writeState2({ ...DEFAULT_STATE });
|
|
@@ -12762,11 +13009,11 @@ function lockFilePath() {
|
|
|
12762
13009
|
function acquireFinalizeLock() {
|
|
12763
13010
|
ensureLearningDir();
|
|
12764
13011
|
const lockPath = lockFilePath();
|
|
12765
|
-
if (
|
|
13012
|
+
if (fs44.existsSync(lockPath)) {
|
|
12766
13013
|
try {
|
|
12767
|
-
const stat =
|
|
13014
|
+
const stat = fs44.statSync(lockPath);
|
|
12768
13015
|
if (Date.now() - stat.mtimeMs < LOCK_STALE_MS) {
|
|
12769
|
-
const pid = parseInt(
|
|
13016
|
+
const pid = parseInt(fs44.readFileSync(lockPath, "utf-8").trim(), 10);
|
|
12770
13017
|
if (!isNaN(pid) && isProcessAlive(pid)) {
|
|
12771
13018
|
return false;
|
|
12772
13019
|
}
|
|
@@ -12774,12 +13021,12 @@ function acquireFinalizeLock() {
|
|
|
12774
13021
|
} catch {
|
|
12775
13022
|
}
|
|
12776
13023
|
try {
|
|
12777
|
-
|
|
13024
|
+
fs44.unlinkSync(lockPath);
|
|
12778
13025
|
} catch {
|
|
12779
13026
|
}
|
|
12780
13027
|
}
|
|
12781
13028
|
try {
|
|
12782
|
-
|
|
13029
|
+
fs44.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
12783
13030
|
return true;
|
|
12784
13031
|
} catch {
|
|
12785
13032
|
return false;
|
|
@@ -12796,13 +13043,13 @@ function isProcessAlive(pid) {
|
|
|
12796
13043
|
function releaseFinalizeLock() {
|
|
12797
13044
|
const lockPath = lockFilePath();
|
|
12798
13045
|
try {
|
|
12799
|
-
if (
|
|
13046
|
+
if (fs44.existsSync(lockPath)) fs44.unlinkSync(lockPath);
|
|
12800
13047
|
} catch {
|
|
12801
13048
|
}
|
|
12802
13049
|
}
|
|
12803
13050
|
|
|
12804
13051
|
// src/lib/notifications.ts
|
|
12805
|
-
import
|
|
13052
|
+
import fs45 from "fs";
|
|
12806
13053
|
import path36 from "path";
|
|
12807
13054
|
import chalk22 from "chalk";
|
|
12808
13055
|
function notificationFilePath() {
|
|
@@ -12811,15 +13058,15 @@ function notificationFilePath() {
|
|
|
12811
13058
|
function writeFinalizeSummary(summary) {
|
|
12812
13059
|
try {
|
|
12813
13060
|
ensureLearningDir();
|
|
12814
|
-
|
|
13061
|
+
fs45.writeFileSync(notificationFilePath(), JSON.stringify(summary, null, 2));
|
|
12815
13062
|
} catch {
|
|
12816
13063
|
}
|
|
12817
13064
|
}
|
|
12818
13065
|
function checkPendingNotifications() {
|
|
12819
13066
|
try {
|
|
12820
|
-
if (!
|
|
12821
|
-
const raw =
|
|
12822
|
-
|
|
13067
|
+
if (!fs45.existsSync(notificationFilePath())) return;
|
|
13068
|
+
const raw = fs45.readFileSync(notificationFilePath(), "utf-8");
|
|
13069
|
+
fs45.unlinkSync(notificationFilePath());
|
|
12823
13070
|
const summary = JSON.parse(raw);
|
|
12824
13071
|
if (!summary.newItemCount || summary.newItemCount === 0) return;
|
|
12825
13072
|
const wasteLabel = summary.wasteTokens > 0 ? ` (~${summary.wasteTokens.toLocaleString()} wasted tokens captured)` : "";
|
|
@@ -12835,7 +13082,7 @@ function checkPendingNotifications() {
|
|
|
12835
13082
|
console.log("");
|
|
12836
13083
|
} catch {
|
|
12837
13084
|
try {
|
|
12838
|
-
|
|
13085
|
+
fs45.unlinkSync(notificationFilePath());
|
|
12839
13086
|
} catch {
|
|
12840
13087
|
}
|
|
12841
13088
|
}
|
|
@@ -12987,7 +13234,7 @@ function calculateSessionWaste(events) {
|
|
|
12987
13234
|
init_config();
|
|
12988
13235
|
|
|
12989
13236
|
// src/learner/roi.ts
|
|
12990
|
-
import
|
|
13237
|
+
import fs46 from "fs";
|
|
12991
13238
|
import path37 from "path";
|
|
12992
13239
|
var DEFAULT_TOTALS = {
|
|
12993
13240
|
totalWasteTokens: 0,
|
|
@@ -13006,15 +13253,15 @@ function roiFilePath() {
|
|
|
13006
13253
|
}
|
|
13007
13254
|
function readROIStats() {
|
|
13008
13255
|
const filePath = roiFilePath();
|
|
13009
|
-
if (!
|
|
13256
|
+
if (!fs46.existsSync(filePath)) {
|
|
13010
13257
|
return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
|
|
13011
13258
|
}
|
|
13012
13259
|
try {
|
|
13013
|
-
return JSON.parse(
|
|
13260
|
+
return JSON.parse(fs46.readFileSync(filePath, "utf-8"));
|
|
13014
13261
|
} catch {
|
|
13015
13262
|
try {
|
|
13016
13263
|
const corruptPath = filePath + ".corrupt";
|
|
13017
|
-
|
|
13264
|
+
fs46.renameSync(filePath, corruptPath);
|
|
13018
13265
|
console.error(`caliber: roi-stats.json was corrupt \u2014 renamed to ${corruptPath}`);
|
|
13019
13266
|
} catch {
|
|
13020
13267
|
}
|
|
@@ -13023,7 +13270,7 @@ function readROIStats() {
|
|
|
13023
13270
|
}
|
|
13024
13271
|
function writeROIStats(stats) {
|
|
13025
13272
|
ensureLearningDir();
|
|
13026
|
-
|
|
13273
|
+
fs46.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
|
|
13027
13274
|
}
|
|
13028
13275
|
function recalculateTotals(stats) {
|
|
13029
13276
|
const totals = stats.totals;
|
|
@@ -13233,20 +13480,27 @@ var INCREMENTAL_INTERVAL = 50;
|
|
|
13233
13480
|
function writeFinalizeError(message) {
|
|
13234
13481
|
try {
|
|
13235
13482
|
const errorPath = path38.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
|
|
13236
|
-
if (!
|
|
13237
|
-
|
|
13238
|
-
|
|
13239
|
-
|
|
13240
|
-
|
|
13241
|
-
|
|
13483
|
+
if (!fs47.existsSync(getLearningDir())) fs47.mkdirSync(getLearningDir(), { recursive: true });
|
|
13484
|
+
fs47.writeFileSync(
|
|
13485
|
+
errorPath,
|
|
13486
|
+
JSON.stringify(
|
|
13487
|
+
{
|
|
13488
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13489
|
+
error: message,
|
|
13490
|
+
pid: process.pid
|
|
13491
|
+
},
|
|
13492
|
+
null,
|
|
13493
|
+
2
|
|
13494
|
+
)
|
|
13495
|
+
);
|
|
13242
13496
|
} catch {
|
|
13243
13497
|
}
|
|
13244
13498
|
}
|
|
13245
13499
|
function readFinalizeError() {
|
|
13246
13500
|
try {
|
|
13247
13501
|
const errorPath = path38.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
|
|
13248
|
-
if (!
|
|
13249
|
-
return JSON.parse(
|
|
13502
|
+
if (!fs47.existsSync(errorPath)) return null;
|
|
13503
|
+
return JSON.parse(fs47.readFileSync(errorPath, "utf-8"));
|
|
13250
13504
|
} catch {
|
|
13251
13505
|
return null;
|
|
13252
13506
|
}
|
|
@@ -13294,17 +13548,19 @@ async function learnObserveCommand(options) {
|
|
|
13294
13548
|
const eventsSinceLastAnalysis = state.eventCount - (state.lastAnalysisEventCount || 0);
|
|
13295
13549
|
if (eventsSinceLastAnalysis >= INCREMENTAL_INTERVAL) {
|
|
13296
13550
|
try {
|
|
13297
|
-
const { resolveCaliber: resolveCaliber2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
|
|
13551
|
+
const { resolveCaliber: resolveCaliber2, isNpxResolution: isNpxResolution2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
|
|
13298
13552
|
const bin = resolveCaliber2();
|
|
13299
13553
|
const { spawn: spawn4 } = await import("child_process");
|
|
13300
13554
|
const logPath = path38.join(getLearningDir(), LEARNING_FINALIZE_LOG);
|
|
13301
|
-
if (!
|
|
13302
|
-
const logFd =
|
|
13303
|
-
|
|
13555
|
+
if (!fs47.existsSync(getLearningDir())) fs47.mkdirSync(getLearningDir(), { recursive: true });
|
|
13556
|
+
const logFd = fs47.openSync(logPath, "a");
|
|
13557
|
+
const NPX_SUFFIX = " --yes @rely-ai/caliber";
|
|
13558
|
+
const [exe, binArgs] = isNpxResolution2() ? [bin.slice(0, -NPX_SUFFIX.length) || "npx", ["--yes", "@rely-ai/caliber"]] : [bin, []];
|
|
13559
|
+
spawn4(exe, [...binArgs, "learn", "finalize", "--auto", "--incremental"], {
|
|
13304
13560
|
detached: true,
|
|
13305
13561
|
stdio: ["ignore", logFd, logFd]
|
|
13306
13562
|
}).unref();
|
|
13307
|
-
|
|
13563
|
+
fs47.closeSync(logFd);
|
|
13308
13564
|
} catch {
|
|
13309
13565
|
}
|
|
13310
13566
|
}
|
|
@@ -13317,7 +13573,8 @@ async function learnFinalizeCommand(options) {
|
|
|
13317
13573
|
if (!options?.force && !isAuto) {
|
|
13318
13574
|
const { isCaliberRunning: isCaliberRunning2 } = await Promise.resolve().then(() => (init_lock(), lock_exports));
|
|
13319
13575
|
if (isCaliberRunning2()) {
|
|
13320
|
-
if (!isAuto)
|
|
13576
|
+
if (!isAuto)
|
|
13577
|
+
console.log(chalk23.dim("caliber: skipping finalize \u2014 another caliber process is running"));
|
|
13321
13578
|
return;
|
|
13322
13579
|
}
|
|
13323
13580
|
}
|
|
@@ -13325,7 +13582,8 @@ async function learnFinalizeCommand(options) {
|
|
|
13325
13582
|
await new Promise((r) => setTimeout(r, AUTO_SETTLE_MS));
|
|
13326
13583
|
}
|
|
13327
13584
|
if (!acquireFinalizeLock()) {
|
|
13328
|
-
if (!isAuto)
|
|
13585
|
+
if (!isAuto)
|
|
13586
|
+
console.log(chalk23.dim("caliber: skipping finalize \u2014 another finalize is in progress"));
|
|
13329
13587
|
return;
|
|
13330
13588
|
}
|
|
13331
13589
|
let analyzed = false;
|
|
@@ -13333,7 +13591,11 @@ async function learnFinalizeCommand(options) {
|
|
|
13333
13591
|
const config = loadConfig();
|
|
13334
13592
|
if (!config) {
|
|
13335
13593
|
if (isAuto) return;
|
|
13336
|
-
console.log(
|
|
13594
|
+
console.log(
|
|
13595
|
+
chalk23.yellow(
|
|
13596
|
+
`caliber: no LLM provider configured \u2014 run \`${resolveCaliber()} config\` first`
|
|
13597
|
+
)
|
|
13598
|
+
);
|
|
13337
13599
|
clearSession();
|
|
13338
13600
|
resetState();
|
|
13339
13601
|
return;
|
|
@@ -13341,7 +13603,12 @@ async function learnFinalizeCommand(options) {
|
|
|
13341
13603
|
const allEvents = readAllEvents();
|
|
13342
13604
|
const threshold = isAuto ? MIN_EVENTS_AUTO : MIN_EVENTS_FOR_ANALYSIS;
|
|
13343
13605
|
if (allEvents.length < threshold) {
|
|
13344
|
-
if (!isAuto)
|
|
13606
|
+
if (!isAuto)
|
|
13607
|
+
console.log(
|
|
13608
|
+
chalk23.dim(
|
|
13609
|
+
`caliber: ${allEvents.length}/${threshold} events recorded \u2014 need more before analysis`
|
|
13610
|
+
)
|
|
13611
|
+
);
|
|
13345
13612
|
return;
|
|
13346
13613
|
}
|
|
13347
13614
|
await validateModel({ fast: true });
|
|
@@ -13350,7 +13617,12 @@ async function learnFinalizeCommand(options) {
|
|
|
13350
13617
|
const analysisOffset = isIncremental ? state.lastAnalysisEventCount || 0 : 0;
|
|
13351
13618
|
const events = analysisOffset > 0 ? allEvents.slice(analysisOffset) : allEvents;
|
|
13352
13619
|
if (events.length < threshold) {
|
|
13353
|
-
if (!isAuto)
|
|
13620
|
+
if (!isAuto)
|
|
13621
|
+
console.log(
|
|
13622
|
+
chalk23.dim(
|
|
13623
|
+
`caliber: ${events.length}/${threshold} new events since last analysis \u2014 need more`
|
|
13624
|
+
)
|
|
13625
|
+
);
|
|
13354
13626
|
return;
|
|
13355
13627
|
}
|
|
13356
13628
|
const existingConfigs = readExistingConfigs(process.cwd());
|
|
@@ -13386,7 +13658,11 @@ async function learnFinalizeCommand(options) {
|
|
|
13386
13658
|
});
|
|
13387
13659
|
} else {
|
|
13388
13660
|
const wasteLabel = waste.totalWasteTokens > 0 ? ` (~${waste.totalWasteTokens.toLocaleString()} wasted tokens captured)` : "";
|
|
13389
|
-
console.log(
|
|
13661
|
+
console.log(
|
|
13662
|
+
chalk23.dim(
|
|
13663
|
+
`caliber: learned ${result.newItemCount} new pattern${result.newItemCount === 1 ? "" : "s"}${wasteLabel}`
|
|
13664
|
+
)
|
|
13665
|
+
);
|
|
13390
13666
|
for (const item of result.newItems) {
|
|
13391
13667
|
console.log(chalk23.dim(` + ${item.replace(/^- /, "").slice(0, 80)}`));
|
|
13392
13668
|
}
|
|
@@ -13481,12 +13757,20 @@ async function learnFinalizeCommand(options) {
|
|
|
13481
13757
|
if (!isIncremental) {
|
|
13482
13758
|
const staleLearnings = findStaleLearnings(roiStats);
|
|
13483
13759
|
if (staleLearnings.length > 0 && !isAuto) {
|
|
13484
|
-
console.log(
|
|
13760
|
+
console.log(
|
|
13761
|
+
chalk23.yellow(
|
|
13762
|
+
`caliber: ${staleLearnings.length} learning${staleLearnings.length === 1 ? "" : "s"} never activated \u2014 run \`${resolveCaliber()} learn list --verbose\` to review`
|
|
13763
|
+
)
|
|
13764
|
+
);
|
|
13485
13765
|
}
|
|
13486
13766
|
}
|
|
13487
13767
|
if (!isAuto && t.estimatedSavingsTokens > 0) {
|
|
13488
13768
|
const totalLearnings = existingLearnedItems + newLearningsProduced;
|
|
13489
|
-
console.log(
|
|
13769
|
+
console.log(
|
|
13770
|
+
chalk23.dim(
|
|
13771
|
+
`caliber: ${totalLearnings} learnings active \u2014 est. ~${t.estimatedSavingsTokens.toLocaleString()} tokens saved across ${t.totalSessionsWithLearnings} sessions`
|
|
13772
|
+
)
|
|
13773
|
+
);
|
|
13490
13774
|
}
|
|
13491
13775
|
} catch (err) {
|
|
13492
13776
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -13511,7 +13795,7 @@ async function learnFinalizeCommand(options) {
|
|
|
13511
13795
|
}
|
|
13512
13796
|
async function learnInstallCommand() {
|
|
13513
13797
|
let anyInstalled = false;
|
|
13514
|
-
if (
|
|
13798
|
+
if (fs47.existsSync(".claude")) {
|
|
13515
13799
|
const r = installLearningHooks();
|
|
13516
13800
|
if (r.installed) {
|
|
13517
13801
|
console.log(chalk23.green("\u2713") + " Claude Code learning hooks installed");
|
|
@@ -13520,7 +13804,7 @@ async function learnInstallCommand() {
|
|
|
13520
13804
|
console.log(chalk23.dim(" Claude Code hooks already installed"));
|
|
13521
13805
|
}
|
|
13522
13806
|
}
|
|
13523
|
-
if (
|
|
13807
|
+
if (fs47.existsSync(".cursor")) {
|
|
13524
13808
|
const r = installCursorLearningHooks();
|
|
13525
13809
|
if (r.installed) {
|
|
13526
13810
|
console.log(chalk23.green("\u2713") + " Cursor learning hooks installed");
|
|
@@ -13529,13 +13813,19 @@ async function learnInstallCommand() {
|
|
|
13529
13813
|
console.log(chalk23.dim(" Cursor hooks already installed"));
|
|
13530
13814
|
}
|
|
13531
13815
|
}
|
|
13532
|
-
if (!
|
|
13816
|
+
if (!fs47.existsSync(".claude") && !fs47.existsSync(".cursor")) {
|
|
13533
13817
|
console.log(chalk23.yellow("No .claude/ or .cursor/ directory found."));
|
|
13534
|
-
console.log(
|
|
13818
|
+
console.log(
|
|
13819
|
+
chalk23.dim(` Run \`${resolveCaliber()} init\` first, or create the directory manually.`)
|
|
13820
|
+
);
|
|
13535
13821
|
return;
|
|
13536
13822
|
}
|
|
13537
13823
|
if (anyInstalled) {
|
|
13538
|
-
console.log(
|
|
13824
|
+
console.log(
|
|
13825
|
+
chalk23.dim(
|
|
13826
|
+
` Tool usage will be recorded and learnings extracted after \u2265${MIN_EVENTS_FOR_ANALYSIS} events.`
|
|
13827
|
+
)
|
|
13828
|
+
);
|
|
13539
13829
|
console.log(chalk23.dim(" Learnings written to CALIBER_LEARNINGS.md."));
|
|
13540
13830
|
}
|
|
13541
13831
|
}
|
|
@@ -13573,7 +13863,9 @@ async function learnStatusCommand() {
|
|
|
13573
13863
|
console.log(chalk23.dim("\u2717") + " Cursor hooks " + chalk23.dim("not installed"));
|
|
13574
13864
|
}
|
|
13575
13865
|
if (!claudeInstalled && !cursorInstalled) {
|
|
13576
|
-
console.log(
|
|
13866
|
+
console.log(
|
|
13867
|
+
chalk23.dim(` Run \`${resolveCaliber()} learn install\` to enable session learning.`)
|
|
13868
|
+
);
|
|
13577
13869
|
}
|
|
13578
13870
|
console.log();
|
|
13579
13871
|
console.log(`Events recorded: ${chalk23.cyan(String(eventCount))}`);
|
|
@@ -13588,7 +13880,7 @@ async function learnStatusCommand() {
|
|
|
13588
13880
|
console.log(`Last error: ${chalk23.red(lastError.error)}`);
|
|
13589
13881
|
console.log(chalk23.dim(` at ${lastError.timestamp}`));
|
|
13590
13882
|
const logPath = path38.join(getLearningDir(), LEARNING_FINALIZE_LOG);
|
|
13591
|
-
if (
|
|
13883
|
+
if (fs47.existsSync(logPath)) {
|
|
13592
13884
|
console.log(chalk23.dim(` Full log: ${logPath}`));
|
|
13593
13885
|
}
|
|
13594
13886
|
}
|
|
@@ -13657,7 +13949,11 @@ async function learnListCommand(options) {
|
|
|
13657
13949
|
async function learnDeleteCommand(indexStr) {
|
|
13658
13950
|
const index = parseInt(indexStr, 10);
|
|
13659
13951
|
if (isNaN(index) || index < 1) {
|
|
13660
|
-
console.log(
|
|
13952
|
+
console.log(
|
|
13953
|
+
chalk23.red(
|
|
13954
|
+
`Invalid index: "${indexStr}". Use a number from \`${resolveCaliber()} learn list\`.`
|
|
13955
|
+
)
|
|
13956
|
+
);
|
|
13661
13957
|
return;
|
|
13662
13958
|
}
|
|
13663
13959
|
const items = getAllLearnings();
|
|
@@ -13668,11 +13964,11 @@ async function learnDeleteCommand(indexStr) {
|
|
|
13668
13964
|
}
|
|
13669
13965
|
const item = items[targetIdx];
|
|
13670
13966
|
const filePath = item.source === "personal" ? PERSONAL_LEARNINGS_FILE : "CALIBER_LEARNINGS.md";
|
|
13671
|
-
if (!
|
|
13967
|
+
if (!fs47.existsSync(filePath)) {
|
|
13672
13968
|
console.log(chalk23.red("Learnings file not found."));
|
|
13673
13969
|
return;
|
|
13674
13970
|
}
|
|
13675
|
-
const content =
|
|
13971
|
+
const content = fs47.readFileSync(filePath, "utf-8");
|
|
13676
13972
|
const lines = content.split("\n");
|
|
13677
13973
|
const bulletsOfSource = items.filter((i) => i.source === item.source);
|
|
13678
13974
|
const posInFile = bulletsOfSource.indexOf(item);
|
|
@@ -13693,9 +13989,9 @@ async function learnDeleteCommand(indexStr) {
|
|
|
13693
13989
|
}
|
|
13694
13990
|
const bulletToRemove = lines[lineToRemove];
|
|
13695
13991
|
const newLines = lines.filter((_, i) => i !== lineToRemove);
|
|
13696
|
-
|
|
13992
|
+
fs47.writeFileSync(filePath, newLines.join("\n"));
|
|
13697
13993
|
if (item.source === "personal") {
|
|
13698
|
-
|
|
13994
|
+
fs47.chmodSync(filePath, 384);
|
|
13699
13995
|
}
|
|
13700
13996
|
const roiStats = readROIStats();
|
|
13701
13997
|
const cleanText = bulletToRemove.replace(/^- /, "").replace(/^\*\*\[[^\]]+\]\*\*\s*/, "").trim();
|
|
@@ -13868,7 +14164,7 @@ async function insightsCommand(options) {
|
|
|
13868
14164
|
}
|
|
13869
14165
|
|
|
13870
14166
|
// src/commands/sources.ts
|
|
13871
|
-
import
|
|
14167
|
+
import fs48 from "fs";
|
|
13872
14168
|
import path39 from "path";
|
|
13873
14169
|
import chalk25 from "chalk";
|
|
13874
14170
|
init_resolve_caliber();
|
|
@@ -13886,9 +14182,9 @@ async function sourcesListCommand() {
|
|
|
13886
14182
|
if (configSources.length > 0) {
|
|
13887
14183
|
for (const source of configSources) {
|
|
13888
14184
|
const sourcePath = source.path || source.url || "";
|
|
13889
|
-
const exists = source.path ?
|
|
14185
|
+
const exists = source.path ? fs48.existsSync(path39.resolve(dir, source.path)) : false;
|
|
13890
14186
|
const status = exists ? chalk25.green("reachable") : chalk25.red("not found");
|
|
13891
|
-
const hasSummary = source.path &&
|
|
14187
|
+
const hasSummary = source.path && fs48.existsSync(path39.join(path39.resolve(dir, source.path), ".caliber", "summary.json"));
|
|
13892
14188
|
console.log(` ${chalk25.bold(source.role || source.type)} ${chalk25.dim(sourcePath)}`);
|
|
13893
14189
|
console.log(` Type: ${source.type} Status: ${status}${hasSummary ? " " + chalk25.cyan("has summary.json") : ""}`);
|
|
13894
14190
|
if (source.description) console.log(` ${chalk25.dim(source.description)}`);
|
|
@@ -13898,7 +14194,7 @@ async function sourcesListCommand() {
|
|
|
13898
14194
|
if (workspaces.length > 0) {
|
|
13899
14195
|
console.log(chalk25.dim(" Auto-detected workspaces:"));
|
|
13900
14196
|
for (const ws of workspaces) {
|
|
13901
|
-
const exists =
|
|
14197
|
+
const exists = fs48.existsSync(path39.resolve(dir, ws));
|
|
13902
14198
|
console.log(` ${exists ? chalk25.green("\u25CF") : chalk25.red("\u25CF")} ${ws}`);
|
|
13903
14199
|
}
|
|
13904
14200
|
console.log("");
|
|
@@ -13907,7 +14203,7 @@ async function sourcesListCommand() {
|
|
|
13907
14203
|
async function sourcesAddCommand(sourcePath) {
|
|
13908
14204
|
const dir = process.cwd();
|
|
13909
14205
|
const absPath = path39.resolve(dir, sourcePath);
|
|
13910
|
-
if (!
|
|
14206
|
+
if (!fs48.existsSync(absPath)) {
|
|
13911
14207
|
console.log(chalk25.red(`
|
|
13912
14208
|
Path not found: ${sourcePath}
|
|
13913
14209
|
`));
|
|
@@ -13970,7 +14266,7 @@ async function sourcesRemoveCommand(name) {
|
|
|
13970
14266
|
}
|
|
13971
14267
|
|
|
13972
14268
|
// src/commands/publish.ts
|
|
13973
|
-
import
|
|
14269
|
+
import fs49 from "fs";
|
|
13974
14270
|
import path40 from "path";
|
|
13975
14271
|
import chalk26 from "chalk";
|
|
13976
14272
|
import ora7 from "ora";
|
|
@@ -14015,11 +14311,11 @@ async function publishCommand() {
|
|
|
14015
14311
|
} catch {
|
|
14016
14312
|
}
|
|
14017
14313
|
const outputDir = path40.join(dir, ".caliber");
|
|
14018
|
-
if (!
|
|
14019
|
-
|
|
14314
|
+
if (!fs49.existsSync(outputDir)) {
|
|
14315
|
+
fs49.mkdirSync(outputDir, { recursive: true });
|
|
14020
14316
|
}
|
|
14021
14317
|
const outputPath = path40.join(outputDir, "summary.json");
|
|
14022
|
-
|
|
14318
|
+
fs49.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + "\n", "utf-8");
|
|
14023
14319
|
spinner.succeed("Project summary published");
|
|
14024
14320
|
console.log(` ${chalk26.green("\u2713")} ${path40.relative(dir, outputPath)}`);
|
|
14025
14321
|
console.log(chalk26.dim("\n Other projects can now reference this repo as a source."));
|
|
@@ -14034,7 +14330,7 @@ async function publishCommand() {
|
|
|
14034
14330
|
|
|
14035
14331
|
// src/commands/bootstrap.ts
|
|
14036
14332
|
init_builtin_skills();
|
|
14037
|
-
import
|
|
14333
|
+
import fs50 from "fs";
|
|
14038
14334
|
import chalk27 from "chalk";
|
|
14039
14335
|
var PLATFORM_SKILL_DIRS = {
|
|
14040
14336
|
claude: ".claude/skills",
|
|
@@ -14055,8 +14351,8 @@ async function bootstrapCommand() {
|
|
|
14055
14351
|
for (const skill of BUILTIN_SKILLS) {
|
|
14056
14352
|
const skillDir = `${skillsDir}/${skill.name}`;
|
|
14057
14353
|
const skillPath = `${skillDir}/SKILL.md`;
|
|
14058
|
-
|
|
14059
|
-
|
|
14354
|
+
fs50.mkdirSync(skillDir, { recursive: true });
|
|
14355
|
+
fs50.writeFileSync(skillPath, buildSkillContent(skill));
|
|
14060
14356
|
written.push(skillPath);
|
|
14061
14357
|
}
|
|
14062
14358
|
}
|
|
@@ -14073,7 +14369,7 @@ async function bootstrapCommand() {
|
|
|
14073
14369
|
}
|
|
14074
14370
|
|
|
14075
14371
|
// src/commands/uninstall.ts
|
|
14076
|
-
import
|
|
14372
|
+
import fs51 from "fs";
|
|
14077
14373
|
import path41 from "path";
|
|
14078
14374
|
import chalk28 from "chalk";
|
|
14079
14375
|
import confirm3 from "@inquirer/confirm";
|
|
@@ -14090,11 +14386,11 @@ var CURSOR_RULES_DIR = path41.join(".cursor", "rules");
|
|
|
14090
14386
|
var CLAUDE_RULES_DIR = path41.join(".claude", "rules");
|
|
14091
14387
|
function removeCaliberManagedFiles(dir, extension) {
|
|
14092
14388
|
const removed = [];
|
|
14093
|
-
if (!
|
|
14094
|
-
for (const file of
|
|
14389
|
+
if (!fs51.existsSync(dir)) return removed;
|
|
14390
|
+
for (const file of fs51.readdirSync(dir)) {
|
|
14095
14391
|
if (file.startsWith(CALIBER_MANAGED_PREFIX) && file.endsWith(extension)) {
|
|
14096
14392
|
const fullPath = path41.join(dir, file);
|
|
14097
|
-
|
|
14393
|
+
fs51.unlinkSync(fullPath);
|
|
14098
14394
|
removed.push(fullPath);
|
|
14099
14395
|
}
|
|
14100
14396
|
}
|
|
@@ -14103,11 +14399,11 @@ function removeCaliberManagedFiles(dir, extension) {
|
|
|
14103
14399
|
function removeBuiltinSkills() {
|
|
14104
14400
|
const removed = [];
|
|
14105
14401
|
for (const skillsDir of SKILL_DIRS) {
|
|
14106
|
-
if (!
|
|
14402
|
+
if (!fs51.existsSync(skillsDir)) continue;
|
|
14107
14403
|
for (const name of BUILTIN_SKILL_NAMES) {
|
|
14108
14404
|
const skillDir = path41.join(skillsDir, name);
|
|
14109
|
-
if (
|
|
14110
|
-
|
|
14405
|
+
if (fs51.existsSync(skillDir)) {
|
|
14406
|
+
fs51.rmSync(skillDir, { recursive: true });
|
|
14111
14407
|
removed.push(skillDir);
|
|
14112
14408
|
}
|
|
14113
14409
|
}
|
|
@@ -14117,15 +14413,15 @@ function removeBuiltinSkills() {
|
|
|
14117
14413
|
function stripManagedBlocksFromFiles() {
|
|
14118
14414
|
const modified = [];
|
|
14119
14415
|
for (const filePath of MANAGED_DOC_FILES) {
|
|
14120
|
-
if (!
|
|
14121
|
-
const original =
|
|
14416
|
+
if (!fs51.existsSync(filePath)) continue;
|
|
14417
|
+
const original = fs51.readFileSync(filePath, "utf-8");
|
|
14122
14418
|
const stripped = stripManagedBlocks(original);
|
|
14123
14419
|
if (stripped !== original) {
|
|
14124
14420
|
const trimmed = stripped.trim();
|
|
14125
14421
|
if (!trimmed || /^#\s*\S*$/.test(trimmed)) {
|
|
14126
|
-
|
|
14422
|
+
fs51.unlinkSync(filePath);
|
|
14127
14423
|
} else {
|
|
14128
|
-
|
|
14424
|
+
fs51.writeFileSync(filePath, stripped);
|
|
14129
14425
|
}
|
|
14130
14426
|
modified.push(filePath);
|
|
14131
14427
|
}
|
|
@@ -14133,8 +14429,8 @@ function stripManagedBlocksFromFiles() {
|
|
|
14133
14429
|
return modified;
|
|
14134
14430
|
}
|
|
14135
14431
|
function removeDirectory(dir) {
|
|
14136
|
-
if (!
|
|
14137
|
-
|
|
14432
|
+
if (!fs51.existsSync(dir)) return false;
|
|
14433
|
+
fs51.rmSync(dir, { recursive: true });
|
|
14138
14434
|
return true;
|
|
14139
14435
|
}
|
|
14140
14436
|
async function uninstallCommand(options) {
|
|
@@ -14206,8 +14502,8 @@ async function uninstallCommand(options) {
|
|
|
14206
14502
|
console.log(` ${chalk28.red("\u2717")} ${skill}/`);
|
|
14207
14503
|
}
|
|
14208
14504
|
if (removedSkills.length > 0) actions.push("builtin skills");
|
|
14209
|
-
if (
|
|
14210
|
-
|
|
14505
|
+
if (fs51.existsSync("CALIBER_LEARNINGS.md")) {
|
|
14506
|
+
fs51.unlinkSync("CALIBER_LEARNINGS.md");
|
|
14211
14507
|
console.log(` ${chalk28.red("\u2717")} CALIBER_LEARNINGS.md`);
|
|
14212
14508
|
actions.push("learnings file");
|
|
14213
14509
|
}
|
|
@@ -14221,18 +14517,18 @@ async function uninstallCommand(options) {
|
|
|
14221
14517
|
}
|
|
14222
14518
|
trackUninstallExecuted();
|
|
14223
14519
|
const configPath = getConfigFilePath();
|
|
14224
|
-
if (
|
|
14520
|
+
if (fs51.existsSync(configPath)) {
|
|
14225
14521
|
console.log("");
|
|
14226
14522
|
const removeConfig = options.force || await confirm3({
|
|
14227
14523
|
message: `Remove global config (~/.caliber/config.json)? This affects all projects.`
|
|
14228
14524
|
});
|
|
14229
14525
|
if (removeConfig) {
|
|
14230
|
-
|
|
14526
|
+
fs51.unlinkSync(configPath);
|
|
14231
14527
|
console.log(` ${chalk28.red("\u2717")} ${configPath}`);
|
|
14232
14528
|
const configDir = path41.dirname(configPath);
|
|
14233
14529
|
try {
|
|
14234
|
-
const remaining =
|
|
14235
|
-
if (remaining.length === 0)
|
|
14530
|
+
const remaining = fs51.readdirSync(configDir);
|
|
14531
|
+
if (remaining.length === 0) fs51.rmdirSync(configDir);
|
|
14236
14532
|
} catch {
|
|
14237
14533
|
}
|
|
14238
14534
|
}
|
|
@@ -14244,7 +14540,7 @@ async function uninstallCommand(options) {
|
|
|
14244
14540
|
|
|
14245
14541
|
// src/cli.ts
|
|
14246
14542
|
var __dirname = path42.dirname(fileURLToPath(import.meta.url));
|
|
14247
|
-
var pkg = JSON.parse(
|
|
14543
|
+
var pkg = JSON.parse(fs52.readFileSync(path42.resolve(__dirname, "..", "package.json"), "utf-8"));
|
|
14248
14544
|
var program = new Command();
|
|
14249
14545
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
14250
14546
|
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 +14651,15 @@ learn.command("delete <index>").description("Delete a learning by its index numb
|
|
|
14355
14651
|
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
14652
|
|
|
14357
14653
|
// src/utils/version-check.ts
|
|
14358
|
-
import
|
|
14654
|
+
import fs53 from "fs";
|
|
14359
14655
|
import path43 from "path";
|
|
14360
14656
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14361
|
-
import { execSync as execSync16, execFileSync as
|
|
14657
|
+
import { execSync as execSync16, execFileSync as execFileSync5 } from "child_process";
|
|
14362
14658
|
import chalk29 from "chalk";
|
|
14363
14659
|
import ora8 from "ora";
|
|
14364
14660
|
import confirm4 from "@inquirer/confirm";
|
|
14365
14661
|
var __dirname_vc = path43.dirname(fileURLToPath2(import.meta.url));
|
|
14366
|
-
var pkg2 = JSON.parse(
|
|
14662
|
+
var pkg2 = JSON.parse(fs53.readFileSync(path43.resolve(__dirname_vc, "..", "package.json"), "utf-8"));
|
|
14367
14663
|
function getChannel(version) {
|
|
14368
14664
|
const match = version.match(/-(dev|next)\./);
|
|
14369
14665
|
return match ? match[1] : "latest";
|
|
@@ -14391,7 +14687,7 @@ function getInstalledVersion() {
|
|
|
14391
14687
|
stdio: ["pipe", "pipe", "pipe"]
|
|
14392
14688
|
}).trim();
|
|
14393
14689
|
const pkgPath = path43.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
14394
|
-
return JSON.parse(
|
|
14690
|
+
return JSON.parse(fs53.readFileSync(pkgPath, "utf-8")).version;
|
|
14395
14691
|
} catch {
|
|
14396
14692
|
return null;
|
|
14397
14693
|
}
|
|
@@ -14439,7 +14735,7 @@ Update available: ${current} -> ${latest}`));
|
|
|
14439
14735
|
if (!/^[\w.-]+$/.test(tag)) return;
|
|
14440
14736
|
const spinner = ora8("Updating caliber...").start();
|
|
14441
14737
|
try {
|
|
14442
|
-
|
|
14738
|
+
execFileSync5("npm", ["install", "-g", `@rely-ai/caliber@${tag}`], {
|
|
14443
14739
|
stdio: "pipe",
|
|
14444
14740
|
timeout: 12e4,
|
|
14445
14741
|
env: { ...process.env, npm_config_fund: "false", npm_config_audit: "false" }
|
|
@@ -14458,7 +14754,7 @@ Update available: ${current} -> ${latest}`));
|
|
|
14458
14754
|
console.log(chalk29.dim(`
|
|
14459
14755
|
Restarting: caliber ${args.join(" ")}
|
|
14460
14756
|
`));
|
|
14461
|
-
|
|
14757
|
+
execFileSync5("caliber", args, {
|
|
14462
14758
|
stdio: "inherit",
|
|
14463
14759
|
env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
|
|
14464
14760
|
});
|