@rely-ai/caliber 1.35.0 → 1.36.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 +249 -74
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -389,7 +389,10 @@ function getCursorSetupRule() {
|
|
|
389
389
|
function stripManagedBlocks(content) {
|
|
390
390
|
let result = content;
|
|
391
391
|
for (const [start, end] of MANAGED_BLOCK_PAIRS) {
|
|
392
|
-
const regex = new RegExp(
|
|
392
|
+
const regex = new RegExp(
|
|
393
|
+
`\\n?${start.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?${end.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n?`,
|
|
394
|
+
"g"
|
|
395
|
+
);
|
|
393
396
|
result = result.replace(regex, "\n");
|
|
394
397
|
}
|
|
395
398
|
return result.replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
@@ -401,7 +404,7 @@ var init_pre_commit_block = __esm({
|
|
|
401
404
|
init_resolve_caliber();
|
|
402
405
|
BLOCK_START = "<!-- caliber:managed:pre-commit -->";
|
|
403
406
|
BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
|
|
404
|
-
MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md";
|
|
407
|
+
MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md .agents/ .opencode/";
|
|
405
408
|
CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
|
|
406
409
|
LEARNINGS_BLOCK_START = "<!-- caliber:managed:learnings -->";
|
|
407
410
|
LEARNINGS_BLOCK_END = "<!-- /caliber:managed:learnings -->";
|
|
@@ -1100,10 +1103,21 @@ __export(lock_exports, {
|
|
|
1100
1103
|
import fs39 from "fs";
|
|
1101
1104
|
import path32 from "path";
|
|
1102
1105
|
import os8 from "os";
|
|
1106
|
+
import crypto5 from "crypto";
|
|
1107
|
+
function buildLockPath() {
|
|
1108
|
+
const cwd = process.cwd();
|
|
1109
|
+
const hash = crypto5.createHash("md5").update(cwd).digest("hex").slice(0, 8);
|
|
1110
|
+
return path32.join(os8.tmpdir(), `.caliber-${hash}.lock`);
|
|
1111
|
+
}
|
|
1112
|
+
function getLockFile() {
|
|
1113
|
+
if (!_lockPath) _lockPath = buildLockPath();
|
|
1114
|
+
return _lockPath;
|
|
1115
|
+
}
|
|
1103
1116
|
function isCaliberRunning() {
|
|
1104
1117
|
try {
|
|
1105
|
-
|
|
1106
|
-
|
|
1118
|
+
const lockFile = buildLockPath();
|
|
1119
|
+
if (!fs39.existsSync(lockFile)) return false;
|
|
1120
|
+
const raw = fs39.readFileSync(lockFile, "utf-8").trim();
|
|
1107
1121
|
const { pid, ts } = JSON.parse(raw);
|
|
1108
1122
|
if (Date.now() - ts > STALE_MS) return false;
|
|
1109
1123
|
try {
|
|
@@ -1118,22 +1132,23 @@ function isCaliberRunning() {
|
|
|
1118
1132
|
}
|
|
1119
1133
|
function acquireLock() {
|
|
1120
1134
|
try {
|
|
1121
|
-
fs39.writeFileSync(
|
|
1135
|
+
fs39.writeFileSync(getLockFile(), JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
1122
1136
|
} catch {
|
|
1123
1137
|
}
|
|
1124
1138
|
}
|
|
1125
1139
|
function releaseLock() {
|
|
1126
1140
|
try {
|
|
1127
|
-
|
|
1141
|
+
const lockFile = getLockFile();
|
|
1142
|
+
if (fs39.existsSync(lockFile)) fs39.unlinkSync(lockFile);
|
|
1128
1143
|
} catch {
|
|
1129
1144
|
}
|
|
1130
1145
|
}
|
|
1131
|
-
var
|
|
1146
|
+
var STALE_MS, _lockPath;
|
|
1132
1147
|
var init_lock = __esm({
|
|
1133
1148
|
"src/lib/lock.ts"() {
|
|
1134
1149
|
"use strict";
|
|
1135
|
-
LOCK_FILE = path32.join(os8.tmpdir(), ".caliber.lock");
|
|
1136
1150
|
STALE_MS = 10 * 60 * 1e3;
|
|
1151
|
+
_lockPath = null;
|
|
1137
1152
|
}
|
|
1138
1153
|
});
|
|
1139
1154
|
|
|
@@ -1291,9 +1306,30 @@ var LEARNING_ROI_FILE = "roi-stats.json";
|
|
|
1291
1306
|
var PERSONAL_LEARNINGS_FILE = path2.join(AUTH_DIR, "personal-learnings.md");
|
|
1292
1307
|
var LEARNING_FINALIZE_LOG = "finalize.log";
|
|
1293
1308
|
var LEARNING_LAST_ERROR_FILE = "last-error.json";
|
|
1309
|
+
var REFRESH_LAST_ERROR_FILE = path2.join(CALIBER_DIR, "last-refresh-error.json");
|
|
1294
1310
|
var MIN_SESSIONS_FOR_COMPARISON = 3;
|
|
1295
1311
|
|
|
1296
1312
|
// src/fingerprint/existing-config.ts
|
|
1313
|
+
function readSkillsFromDir(skillsDir) {
|
|
1314
|
+
if (!fs2.existsSync(skillsDir)) return void 0;
|
|
1315
|
+
try {
|
|
1316
|
+
const skills = fs2.readdirSync(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).reduce((acc, entry) => {
|
|
1317
|
+
const skillPath = path3.join(skillsDir, entry.name, "SKILL.md");
|
|
1318
|
+
try {
|
|
1319
|
+
acc.push({
|
|
1320
|
+
name: entry.name,
|
|
1321
|
+
filename: "SKILL.md",
|
|
1322
|
+
content: fs2.readFileSync(skillPath, "utf-8")
|
|
1323
|
+
});
|
|
1324
|
+
} catch {
|
|
1325
|
+
}
|
|
1326
|
+
return acc;
|
|
1327
|
+
}, []);
|
|
1328
|
+
return skills.length > 0 ? skills : void 0;
|
|
1329
|
+
} catch {
|
|
1330
|
+
return void 0;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1297
1333
|
function readExistingConfigs(dir) {
|
|
1298
1334
|
const configs = {};
|
|
1299
1335
|
const readmeMdPath = path3.join(dir, "README.md");
|
|
@@ -1324,7 +1360,10 @@ function readExistingConfigs(dir) {
|
|
|
1324
1360
|
const entryPath = path3.join(skillsDir, entry);
|
|
1325
1361
|
const skillMdPath = path3.join(entryPath, "SKILL.md");
|
|
1326
1362
|
if (fs2.statSync(entryPath).isDirectory() && fs2.existsSync(skillMdPath)) {
|
|
1327
|
-
skills.push({
|
|
1363
|
+
skills.push({
|
|
1364
|
+
filename: `${entry}/SKILL.md`,
|
|
1365
|
+
content: fs2.readFileSync(skillMdPath, "utf-8")
|
|
1366
|
+
});
|
|
1328
1367
|
} else if (entry.endsWith(".md")) {
|
|
1329
1368
|
skills.push({ filename: entry, content: fs2.readFileSync(entryPath, "utf-8") });
|
|
1330
1369
|
}
|
|
@@ -1348,20 +1387,26 @@ function readExistingConfigs(dir) {
|
|
|
1348
1387
|
} catch {
|
|
1349
1388
|
}
|
|
1350
1389
|
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1390
|
+
configs.cursorSkills = readSkillsFromDir(path3.join(dir, ".cursor", "skills"));
|
|
1391
|
+
const copilotPath = path3.join(dir, ".github", "copilot-instructions.md");
|
|
1392
|
+
if (fs2.existsSync(copilotPath)) {
|
|
1393
|
+
configs.copilotInstructions = fs2.readFileSync(copilotPath, "utf-8");
|
|
1394
|
+
}
|
|
1395
|
+
const copilotInstructionsDir = path3.join(dir, ".github", "instructions");
|
|
1396
|
+
if (fs2.existsSync(copilotInstructionsDir)) {
|
|
1353
1397
|
try {
|
|
1354
|
-
const
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
}));
|
|
1398
|
+
const files = fs2.readdirSync(copilotInstructionsDir).filter((f) => f.endsWith(".instructions.md"));
|
|
1399
|
+
if (files.length > 0) {
|
|
1400
|
+
configs.copilotInstructionFiles = files.map((f) => ({
|
|
1401
|
+
filename: f,
|
|
1402
|
+
content: fs2.readFileSync(path3.join(copilotInstructionsDir, f), "utf-8")
|
|
1403
|
+
}));
|
|
1404
|
+
}
|
|
1362
1405
|
} catch {
|
|
1363
1406
|
}
|
|
1364
1407
|
}
|
|
1408
|
+
configs.codexSkills = readSkillsFromDir(path3.join(dir, ".agents", "skills"));
|
|
1409
|
+
configs.opencodeSkills = readSkillsFromDir(path3.join(dir, ".opencode", "skills"));
|
|
1365
1410
|
const mcpJsonPath = path3.join(dir, ".mcp.json");
|
|
1366
1411
|
if (fs2.existsSync(mcpJsonPath)) {
|
|
1367
1412
|
try {
|
|
@@ -3325,6 +3370,11 @@ Quality constraints (the output is scored deterministically):
|
|
|
3325
3370
|
- ONLY reference file paths that exist in the provided file tree \u2014 do NOT invent paths
|
|
3326
3371
|
- Preserve the existing structure (headings, bullet style, formatting)
|
|
3327
3372
|
|
|
3373
|
+
Cross-agent sync:
|
|
3374
|
+
- When a code change affects documentation, update ALL provided platform configs together.
|
|
3375
|
+
- A renamed command, moved file, or changed convention must be reflected in every config (CLAUDE.md, AGENTS.md, copilot instructions, skills across all platforms).
|
|
3376
|
+
- Cross-agent consistency is critical \u2014 all agents working on this repo must have the same, accurate context.
|
|
3377
|
+
|
|
3328
3378
|
Managed content:
|
|
3329
3379
|
- Keep managed blocks (<!-- caliber:managed --> ... <!-- /caliber:managed -->) intact
|
|
3330
3380
|
- Do NOT modify CALIBER_LEARNINGS.md \u2014 it is managed separately
|
|
@@ -3338,7 +3388,6 @@ Return a JSON object with this exact shape:
|
|
|
3338
3388
|
"readmeMd": "<updated content or null>",
|
|
3339
3389
|
"cursorrules": "<updated content or null>",
|
|
3340
3390
|
"cursorRules": [{"filename": "name.mdc", "content": "..."}] or null,
|
|
3341
|
-
"claudeSkills": [{"filename": "name.md", "content": "..."}] or null,
|
|
3342
3391
|
"copilotInstructions": "<updated content or null>",
|
|
3343
3392
|
"copilotInstructionFiles": [{"filename": "name.instructions.md", "content": "..."}] or null
|
|
3344
3393
|
},
|
|
@@ -3930,7 +3979,7 @@ var STOP_HOOK_SCRIPT_CONTENT = `#!/bin/sh
|
|
|
3930
3979
|
if grep -q "caliber" .git/hooks/pre-commit 2>/dev/null; then
|
|
3931
3980
|
exit 0
|
|
3932
3981
|
fi
|
|
3933
|
-
FLAG="/tmp/caliber-nudge-$(echo "$PWD" | shasum | cut -c1-8)"
|
|
3982
|
+
FLAG="/tmp/caliber-nudge-$(echo "$PWD" | (shasum 2>/dev/null || sha1sum 2>/dev/null || md5sum 2>/dev/null || cksum) | cut -c1-8)"
|
|
3934
3983
|
find /tmp -maxdepth 1 -name "caliber-nudge-*" -mmin +120 -delete 2>/dev/null
|
|
3935
3984
|
if [ -f "$FLAG" ]; then
|
|
3936
3985
|
exit 0
|
|
@@ -3961,11 +4010,13 @@ function installStopHook() {
|
|
|
3961
4010
|
}
|
|
3962
4011
|
settings.hooks.Stop.push({
|
|
3963
4012
|
matcher: "",
|
|
3964
|
-
hooks: [
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
4013
|
+
hooks: [
|
|
4014
|
+
{
|
|
4015
|
+
type: "command",
|
|
4016
|
+
command: STOP_HOOK_SCRIPT_PATH,
|
|
4017
|
+
description: STOP_HOOK_DESCRIPTION
|
|
4018
|
+
}
|
|
4019
|
+
]
|
|
3969
4020
|
});
|
|
3970
4021
|
writeSettings(settings);
|
|
3971
4022
|
return { installed: true, alreadyInstalled: false };
|
|
@@ -3997,16 +4048,20 @@ function getPrecommitBlock() {
|
|
|
3997
4048
|
const invoke = npx ? bin : `"${bin}"`;
|
|
3998
4049
|
return `${PRECOMMIT_START}
|
|
3999
4050
|
if ${guard}; then
|
|
4051
|
+
mkdir -p .caliber
|
|
4000
4052
|
echo "\\033[2mcaliber: refreshing docs...\\033[0m"
|
|
4001
|
-
${invoke} refresh 2
|
|
4002
|
-
${invoke} learn finalize 2
|
|
4003
|
-
git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md 2>/dev/null | xargs git add 2>/dev/null || true
|
|
4053
|
+
${invoke} refresh --quiet 2>.caliber/refresh-hook.log || true
|
|
4054
|
+
${invoke} learn finalize 2>>.caliber/refresh-hook.log || true
|
|
4055
|
+
git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md .github/ .agents/ .opencode/ 2>/dev/null | xargs git add 2>/dev/null || true
|
|
4004
4056
|
fi
|
|
4005
4057
|
${PRECOMMIT_END}`;
|
|
4006
4058
|
}
|
|
4007
4059
|
function getGitHooksDir() {
|
|
4008
4060
|
try {
|
|
4009
|
-
const gitDir = execSync8("git rev-parse --git-dir", {
|
|
4061
|
+
const gitDir = execSync8("git rev-parse --git-dir", {
|
|
4062
|
+
encoding: "utf-8",
|
|
4063
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4064
|
+
}).trim();
|
|
4010
4065
|
return path10.join(gitDir, "hooks");
|
|
4011
4066
|
} catch {
|
|
4012
4067
|
return null;
|
|
@@ -5294,7 +5349,10 @@ import fs14 from "fs";
|
|
|
5294
5349
|
import path14 from "path";
|
|
5295
5350
|
function writeCodexConfig(config) {
|
|
5296
5351
|
const written = [];
|
|
5297
|
-
fs14.writeFileSync(
|
|
5352
|
+
fs14.writeFileSync(
|
|
5353
|
+
"AGENTS.md",
|
|
5354
|
+
appendLearningsBlock(appendPreCommitBlock(config.agentsMd, "codex"))
|
|
5355
|
+
);
|
|
5298
5356
|
written.push("AGENTS.md");
|
|
5299
5357
|
if (config.skills?.length) {
|
|
5300
5358
|
for (const skill of config.skills) {
|
|
@@ -5323,7 +5381,10 @@ function writeGithubCopilotConfig(config) {
|
|
|
5323
5381
|
const written = [];
|
|
5324
5382
|
if (config.instructions) {
|
|
5325
5383
|
fs15.mkdirSync(".github", { recursive: true });
|
|
5326
|
-
fs15.writeFileSync(
|
|
5384
|
+
fs15.writeFileSync(
|
|
5385
|
+
path15.join(".github", "copilot-instructions.md"),
|
|
5386
|
+
appendLearningsBlock(appendPreCommitBlock(config.instructions, "copilot"))
|
|
5387
|
+
);
|
|
5327
5388
|
written.push(".github/copilot-instructions.md");
|
|
5328
5389
|
}
|
|
5329
5390
|
if (config.instructionFiles?.length) {
|
|
@@ -5345,7 +5406,10 @@ import path17 from "path";
|
|
|
5345
5406
|
function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
|
|
5346
5407
|
const written = [];
|
|
5347
5408
|
if (!agentsMdAlreadyWritten) {
|
|
5348
|
-
fs17.writeFileSync(
|
|
5409
|
+
fs17.writeFileSync(
|
|
5410
|
+
"AGENTS.md",
|
|
5411
|
+
appendLearningsBlock(appendPreCommitBlock(config.agentsMd, "codex"))
|
|
5412
|
+
);
|
|
5349
5413
|
written.push("AGENTS.md");
|
|
5350
5414
|
}
|
|
5351
5415
|
if (config.skills?.length) {
|
|
@@ -10660,12 +10724,23 @@ import { execSync as execSync15 } from "child_process";
|
|
|
10660
10724
|
var MAX_DIFF_BYTES = 1e5;
|
|
10661
10725
|
var DOC_PATTERNS = [
|
|
10662
10726
|
"CLAUDE.md",
|
|
10727
|
+
"AGENTS.md",
|
|
10663
10728
|
"README.md",
|
|
10664
10729
|
".cursorrules",
|
|
10665
10730
|
".cursor/rules/",
|
|
10731
|
+
".cursor/skills/",
|
|
10666
10732
|
".claude/skills/",
|
|
10733
|
+
".agents/skills/",
|
|
10734
|
+
".opencode/skills/",
|
|
10735
|
+
".github/copilot-instructions.md",
|
|
10736
|
+
".github/instructions/",
|
|
10667
10737
|
"CALIBER_LEARNINGS.md"
|
|
10668
10738
|
];
|
|
10739
|
+
function truncateAtLineEnd(text, maxBytes) {
|
|
10740
|
+
if (text.length <= maxBytes) return text;
|
|
10741
|
+
const lastNewline = text.lastIndexOf("\n", maxBytes);
|
|
10742
|
+
return lastNewline === -1 ? text.slice(0, maxBytes) : text.slice(0, lastNewline);
|
|
10743
|
+
}
|
|
10669
10744
|
function excludeArgs() {
|
|
10670
10745
|
return DOC_PATTERNS.flatMap((p) => ["--", `:!${p}`]);
|
|
10671
10746
|
}
|
|
@@ -10714,9 +10789,9 @@ function collectDiff(lastSha) {
|
|
|
10714
10789
|
const totalSize = committedDiff.length + stagedDiff.length + unstagedDiff.length;
|
|
10715
10790
|
if (totalSize > MAX_DIFF_BYTES) {
|
|
10716
10791
|
const ratio = MAX_DIFF_BYTES / totalSize;
|
|
10717
|
-
committedDiff = committedDiff
|
|
10718
|
-
stagedDiff = stagedDiff
|
|
10719
|
-
unstagedDiff = unstagedDiff
|
|
10792
|
+
committedDiff = truncateAtLineEnd(committedDiff, Math.floor(committedDiff.length * ratio));
|
|
10793
|
+
stagedDiff = truncateAtLineEnd(stagedDiff, Math.floor(stagedDiff.length * ratio));
|
|
10794
|
+
unstagedDiff = truncateAtLineEnd(unstagedDiff, Math.floor(unstagedDiff.length * ratio));
|
|
10720
10795
|
}
|
|
10721
10796
|
const hasChanges = !!(committedDiff || stagedDiff || unstagedDiff || changedFiles.length);
|
|
10722
10797
|
const parts = [];
|
|
@@ -10735,11 +10810,11 @@ import path30 from "path";
|
|
|
10735
10810
|
function writeRefreshDocs(docs) {
|
|
10736
10811
|
const written = [];
|
|
10737
10812
|
if (docs.agentsMd) {
|
|
10738
|
-
fs37.writeFileSync("AGENTS.md",
|
|
10813
|
+
fs37.writeFileSync("AGENTS.md", appendManagedBlocks(docs.agentsMd, "codex"));
|
|
10739
10814
|
written.push("AGENTS.md");
|
|
10740
10815
|
}
|
|
10741
10816
|
if (docs.claudeMd) {
|
|
10742
|
-
fs37.writeFileSync("CLAUDE.md",
|
|
10817
|
+
fs37.writeFileSync("CLAUDE.md", appendManagedBlocks(docs.claudeMd));
|
|
10743
10818
|
written.push("CLAUDE.md");
|
|
10744
10819
|
}
|
|
10745
10820
|
if (docs.readmeMd) {
|
|
@@ -10758,17 +10833,12 @@ function writeRefreshDocs(docs) {
|
|
|
10758
10833
|
written.push(`.cursor/rules/${rule.filename}`);
|
|
10759
10834
|
}
|
|
10760
10835
|
}
|
|
10761
|
-
if (docs.claudeSkills) {
|
|
10762
|
-
const skillsDir = path30.join(".claude", "skills");
|
|
10763
|
-
if (!fs37.existsSync(skillsDir)) fs37.mkdirSync(skillsDir, { recursive: true });
|
|
10764
|
-
for (const skill of docs.claudeSkills) {
|
|
10765
|
-
fs37.writeFileSync(path30.join(skillsDir, skill.filename), skill.content);
|
|
10766
|
-
written.push(`.claude/skills/${skill.filename}`);
|
|
10767
|
-
}
|
|
10768
|
-
}
|
|
10769
10836
|
if (docs.copilotInstructions) {
|
|
10770
10837
|
fs37.mkdirSync(".github", { recursive: true });
|
|
10771
|
-
fs37.writeFileSync(
|
|
10838
|
+
fs37.writeFileSync(
|
|
10839
|
+
path30.join(".github", "copilot-instructions.md"),
|
|
10840
|
+
appendManagedBlocks(docs.copilotInstructions, "copilot")
|
|
10841
|
+
);
|
|
10772
10842
|
written.push(".github/copilot-instructions.md");
|
|
10773
10843
|
}
|
|
10774
10844
|
if (docs.copilotInstructionFiles) {
|
|
@@ -10784,6 +10854,7 @@ function writeRefreshDocs(docs) {
|
|
|
10784
10854
|
|
|
10785
10855
|
// src/ai/refresh.ts
|
|
10786
10856
|
init_config();
|
|
10857
|
+
init_pre_commit_block();
|
|
10787
10858
|
async function refreshDocs(diff, existingDocs, projectContext, learnedSection, sources2) {
|
|
10788
10859
|
const prompt = buildRefreshPrompt(diff, existingDocs, projectContext, learnedSection, sources2);
|
|
10789
10860
|
const fastModel = getFastModel();
|
|
@@ -10799,13 +10870,17 @@ function buildRefreshPrompt(diff, existingDocs, projectContext, learnedSection,
|
|
|
10799
10870
|
const parts = [];
|
|
10800
10871
|
parts.push("Update documentation based on the following code changes.\n");
|
|
10801
10872
|
if (projectContext.packageName) parts.push(`Project: ${projectContext.packageName}`);
|
|
10802
|
-
if (projectContext.languages?.length)
|
|
10803
|
-
|
|
10873
|
+
if (projectContext.languages?.length)
|
|
10874
|
+
parts.push(`Languages: ${projectContext.languages.join(", ")}`);
|
|
10875
|
+
if (projectContext.frameworks?.length)
|
|
10876
|
+
parts.push(`Frameworks: ${projectContext.frameworks.join(", ")}`);
|
|
10804
10877
|
if (projectContext.fileTree?.length) {
|
|
10805
10878
|
const tree = projectContext.fileTree.slice(0, 200);
|
|
10806
|
-
parts.push(
|
|
10879
|
+
parts.push(
|
|
10880
|
+
`
|
|
10807
10881
|
File tree (${tree.length}/${projectContext.fileTree.length} \u2014 only reference paths from this list):
|
|
10808
|
-
${tree.join("\n")}`
|
|
10882
|
+
${tree.join("\n")}`
|
|
10883
|
+
);
|
|
10809
10884
|
}
|
|
10810
10885
|
parts.push(`
|
|
10811
10886
|
Changed files: ${diff.changedFiles.join(", ")}`);
|
|
@@ -10825,11 +10900,11 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
10825
10900
|
parts.push("\n--- Current Documentation ---");
|
|
10826
10901
|
if (existingDocs.agentsMd) {
|
|
10827
10902
|
parts.push("\n[AGENTS.md]");
|
|
10828
|
-
parts.push(existingDocs.agentsMd);
|
|
10903
|
+
parts.push(stripManagedBlocks(existingDocs.agentsMd));
|
|
10829
10904
|
}
|
|
10830
10905
|
if (existingDocs.claudeMd) {
|
|
10831
10906
|
parts.push("\n[CLAUDE.md]");
|
|
10832
|
-
parts.push(existingDocs.claudeMd);
|
|
10907
|
+
parts.push(stripManagedBlocks(existingDocs.claudeMd));
|
|
10833
10908
|
}
|
|
10834
10909
|
if (existingDocs.readmeMd) {
|
|
10835
10910
|
parts.push("\n[README.md]");
|
|
@@ -10839,20 +10914,25 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
10839
10914
|
parts.push("\n[.cursorrules]");
|
|
10840
10915
|
parts.push(existingDocs.cursorrules);
|
|
10841
10916
|
}
|
|
10842
|
-
if (existingDocs.claudeSkills?.length) {
|
|
10843
|
-
for (const skill of existingDocs.claudeSkills) {
|
|
10844
|
-
parts.push(`
|
|
10845
|
-
[.claude/skills/${skill.filename}]`);
|
|
10846
|
-
parts.push(skill.content);
|
|
10847
|
-
}
|
|
10848
|
-
}
|
|
10849
10917
|
if (existingDocs.cursorRules?.length) {
|
|
10850
10918
|
for (const rule of existingDocs.cursorRules) {
|
|
10919
|
+
if (rule.filename.startsWith("caliber-")) continue;
|
|
10851
10920
|
parts.push(`
|
|
10852
10921
|
[.cursor/rules/${rule.filename}]`);
|
|
10853
10922
|
parts.push(rule.content);
|
|
10854
10923
|
}
|
|
10855
10924
|
}
|
|
10925
|
+
if (existingDocs.copilotInstructions) {
|
|
10926
|
+
parts.push("\n[.github/copilot-instructions.md]");
|
|
10927
|
+
parts.push(stripManagedBlocks(existingDocs.copilotInstructions));
|
|
10928
|
+
}
|
|
10929
|
+
if (existingDocs.copilotInstructionFiles?.length) {
|
|
10930
|
+
for (const file of existingDocs.copilotInstructionFiles) {
|
|
10931
|
+
parts.push(`
|
|
10932
|
+
[.github/instructions/${file.filename}]`);
|
|
10933
|
+
parts.push(file.content);
|
|
10934
|
+
}
|
|
10935
|
+
}
|
|
10856
10936
|
if (learnedSection) {
|
|
10857
10937
|
parts.push("\n--- Learned Patterns (from session learning) ---");
|
|
10858
10938
|
parts.push("Consider these accumulated learnings when deciding what to update:");
|
|
@@ -11062,6 +11142,40 @@ function migrateInlineLearnings() {
|
|
|
11062
11142
|
init_config();
|
|
11063
11143
|
init_resolve_caliber();
|
|
11064
11144
|
init_builtin_skills();
|
|
11145
|
+
function writeRefreshError(error) {
|
|
11146
|
+
try {
|
|
11147
|
+
if (!fs40.existsSync(CALIBER_DIR)) fs40.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
11148
|
+
fs40.writeFileSync(
|
|
11149
|
+
REFRESH_LAST_ERROR_FILE,
|
|
11150
|
+
JSON.stringify(
|
|
11151
|
+
{
|
|
11152
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11153
|
+
error: error instanceof Error ? error.message : String(error),
|
|
11154
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
11155
|
+
cwd: process.cwd(),
|
|
11156
|
+
nodeVersion: process.version
|
|
11157
|
+
},
|
|
11158
|
+
null,
|
|
11159
|
+
2
|
|
11160
|
+
)
|
|
11161
|
+
);
|
|
11162
|
+
} catch {
|
|
11163
|
+
}
|
|
11164
|
+
}
|
|
11165
|
+
function readRefreshError() {
|
|
11166
|
+
try {
|
|
11167
|
+
if (!fs40.existsSync(REFRESH_LAST_ERROR_FILE)) return null;
|
|
11168
|
+
return JSON.parse(fs40.readFileSync(REFRESH_LAST_ERROR_FILE, "utf-8"));
|
|
11169
|
+
} catch {
|
|
11170
|
+
return null;
|
|
11171
|
+
}
|
|
11172
|
+
}
|
|
11173
|
+
function clearRefreshError() {
|
|
11174
|
+
try {
|
|
11175
|
+
if (fs40.existsSync(REFRESH_LAST_ERROR_FILE)) fs40.unlinkSync(REFRESH_LAST_ERROR_FILE);
|
|
11176
|
+
} catch {
|
|
11177
|
+
}
|
|
11178
|
+
}
|
|
11065
11179
|
function log2(quiet, ...args) {
|
|
11066
11180
|
if (!quiet) console.log(...args);
|
|
11067
11181
|
}
|
|
@@ -11080,21 +11194,41 @@ function discoverGitRepos(parentDir) {
|
|
|
11080
11194
|
}
|
|
11081
11195
|
return repos.sort();
|
|
11082
11196
|
}
|
|
11197
|
+
function collectFilesToWrite(updatedDocs) {
|
|
11198
|
+
const files = [];
|
|
11199
|
+
if (updatedDocs.agentsMd) files.push("AGENTS.md");
|
|
11200
|
+
if (updatedDocs.claudeMd) files.push("CLAUDE.md");
|
|
11201
|
+
if (updatedDocs.readmeMd) files.push("README.md");
|
|
11202
|
+
if (updatedDocs.cursorrules) files.push(".cursorrules");
|
|
11203
|
+
if (Array.isArray(updatedDocs.cursorRules)) {
|
|
11204
|
+
for (const r of updatedDocs.cursorRules)
|
|
11205
|
+
files.push(`.cursor/rules/${r.filename}`);
|
|
11206
|
+
}
|
|
11207
|
+
if (updatedDocs.copilotInstructions) files.push(".github/copilot-instructions.md");
|
|
11208
|
+
if (Array.isArray(updatedDocs.copilotInstructionFiles)) {
|
|
11209
|
+
for (const f of updatedDocs.copilotInstructionFiles)
|
|
11210
|
+
files.push(`.github/instructions/${f.filename}`);
|
|
11211
|
+
}
|
|
11212
|
+
return files;
|
|
11213
|
+
}
|
|
11083
11214
|
var REFRESH_COOLDOWN_MS = 3e4;
|
|
11084
11215
|
async function refreshSingleRepo(repoDir, options) {
|
|
11085
11216
|
const quiet = !!options.quiet;
|
|
11086
11217
|
const prefix = options.label ? `${chalk19.bold(options.label)} ` : "";
|
|
11087
11218
|
const state = readState();
|
|
11088
11219
|
const lastSha = state?.lastRefreshSha ?? null;
|
|
11089
|
-
|
|
11220
|
+
const currentSha = getCurrentHeadSha();
|
|
11221
|
+
if (state?.lastRefreshTimestamp && lastSha && currentSha === lastSha) {
|
|
11090
11222
|
const elapsed = Date.now() - new Date(state.lastRefreshTimestamp).getTime();
|
|
11091
11223
|
if (elapsed < REFRESH_COOLDOWN_MS && elapsed > 0) {
|
|
11092
|
-
log2(
|
|
11224
|
+
log2(
|
|
11225
|
+
quiet,
|
|
11226
|
+
chalk19.dim(`${prefix}Skipped \u2014 last refresh was ${Math.round(elapsed / 1e3)}s ago.`)
|
|
11227
|
+
);
|
|
11093
11228
|
return;
|
|
11094
11229
|
}
|
|
11095
11230
|
}
|
|
11096
11231
|
const diff = collectDiff(lastSha);
|
|
11097
|
-
const currentSha = getCurrentHeadSha();
|
|
11098
11232
|
if (!diff.hasChanges) {
|
|
11099
11233
|
if (currentSha) {
|
|
11100
11234
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -11103,9 +11237,9 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11103
11237
|
return;
|
|
11104
11238
|
}
|
|
11105
11239
|
const spinner = quiet ? null : ora6(`${prefix}Analyzing changes...`).start();
|
|
11106
|
-
const existingDocs = readExistingConfigs(repoDir);
|
|
11107
11240
|
const learnedSection = readLearnedSection();
|
|
11108
11241
|
const fingerprint = await collectFingerprint(repoDir);
|
|
11242
|
+
const existingDocs = fingerprint.existingConfigs;
|
|
11109
11243
|
const projectContext = {
|
|
11110
11244
|
languages: fingerprint.languages,
|
|
11111
11245
|
frameworks: fingerprint.frameworks,
|
|
@@ -11124,12 +11258,24 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11124
11258
|
const sourcesPayload = sources2.length > 0 ? sources2 : void 0;
|
|
11125
11259
|
let response;
|
|
11126
11260
|
try {
|
|
11127
|
-
response = await refreshDocs(
|
|
11261
|
+
response = await refreshDocs(
|
|
11262
|
+
diffPayload,
|
|
11263
|
+
existingDocs,
|
|
11264
|
+
projectContext,
|
|
11265
|
+
learnedSection,
|
|
11266
|
+
sourcesPayload
|
|
11267
|
+
);
|
|
11128
11268
|
} catch (firstErr) {
|
|
11129
11269
|
const isTransient = firstErr instanceof Error && TRANSIENT_ERRORS.some((e) => firstErr.message.toLowerCase().includes(e.toLowerCase()));
|
|
11130
11270
|
if (!isTransient) throw firstErr;
|
|
11131
11271
|
try {
|
|
11132
|
-
response = await refreshDocs(
|
|
11272
|
+
response = await refreshDocs(
|
|
11273
|
+
diffPayload,
|
|
11274
|
+
existingDocs,
|
|
11275
|
+
projectContext,
|
|
11276
|
+
learnedSection,
|
|
11277
|
+
sourcesPayload
|
|
11278
|
+
);
|
|
11133
11279
|
} catch {
|
|
11134
11280
|
spinner?.fail(`${prefix}Refresh failed after retry`);
|
|
11135
11281
|
throw firstErr;
|
|
@@ -11155,9 +11301,9 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11155
11301
|
}
|
|
11156
11302
|
const targetAgent = state?.targetAgent ?? detectTargetAgent(repoDir);
|
|
11157
11303
|
const preScore = computeLocalScore(repoDir, targetAgent);
|
|
11158
|
-
const
|
|
11304
|
+
const allFilesToWrite = collectFilesToWrite(response.updatedDocs);
|
|
11159
11305
|
const preRefreshContents = /* @__PURE__ */ new Map();
|
|
11160
|
-
for (const filePath of
|
|
11306
|
+
for (const filePath of allFilesToWrite) {
|
|
11161
11307
|
const fullPath = path33.resolve(repoDir, filePath);
|
|
11162
11308
|
try {
|
|
11163
11309
|
preRefreshContents.set(filePath, fs40.readFileSync(fullPath, "utf-8"));
|
|
@@ -11181,7 +11327,9 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11181
11327
|
fs40.writeFileSync(fullPath, content);
|
|
11182
11328
|
}
|
|
11183
11329
|
}
|
|
11184
|
-
spinner?.warn(
|
|
11330
|
+
spinner?.warn(
|
|
11331
|
+
`${prefix}Refresh reverted \u2014 score would drop from ${preScore.score} to ${postScore.score}`
|
|
11332
|
+
);
|
|
11185
11333
|
log2(quiet, chalk19.dim(` Config quality gate prevented a regression. No files were changed.`));
|
|
11186
11334
|
if (currentSha) {
|
|
11187
11335
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -11201,6 +11349,7 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11201
11349
|
for (const file of builtinWritten) {
|
|
11202
11350
|
log2(quiet, ` ${chalk19.green("\u2713")} ${file} ${chalk19.dim("(built-in)")}`);
|
|
11203
11351
|
}
|
|
11352
|
+
clearRefreshError();
|
|
11204
11353
|
if (currentSha) {
|
|
11205
11354
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
11206
11355
|
}
|
|
@@ -11211,11 +11360,28 @@ async function refreshCommand(options) {
|
|
|
11211
11360
|
const { isCaliberRunning: isCaliberRunning2 } = await Promise.resolve().then(() => (init_lock(), lock_exports));
|
|
11212
11361
|
if (isCaliberRunning2()) return;
|
|
11213
11362
|
}
|
|
11363
|
+
if (!quiet) {
|
|
11364
|
+
const lastError = readRefreshError();
|
|
11365
|
+
if (lastError) {
|
|
11366
|
+
console.log(chalk19.yellow(`
|
|
11367
|
+
\u26A0 Last refresh failed (${lastError.timestamp}):`));
|
|
11368
|
+
console.log(chalk19.dim(` ${lastError.error}`));
|
|
11369
|
+
console.log(
|
|
11370
|
+
chalk19.dim(
|
|
11371
|
+
` Run with --debug for full details, or report at https://github.com/caliber-ai-org/ai-setup/issues
|
|
11372
|
+
`
|
|
11373
|
+
)
|
|
11374
|
+
);
|
|
11375
|
+
clearRefreshError();
|
|
11376
|
+
}
|
|
11377
|
+
}
|
|
11214
11378
|
try {
|
|
11215
11379
|
const config = loadConfig();
|
|
11216
11380
|
if (!config) {
|
|
11217
11381
|
if (quiet) return;
|
|
11218
|
-
console.log(
|
|
11382
|
+
console.log(
|
|
11383
|
+
chalk19.red("No LLM provider configured. Run ") + chalk19.hex("#83D1EB")(`${resolveCaliber()} config`) + chalk19.red(" (e.g. choose Cursor) or set an API key.")
|
|
11384
|
+
);
|
|
11219
11385
|
throw new Error("__exit__");
|
|
11220
11386
|
}
|
|
11221
11387
|
await validateModel({ fast: true });
|
|
@@ -11226,7 +11392,9 @@ async function refreshCommand(options) {
|
|
|
11226
11392
|
const repos = discoverGitRepos(process.cwd());
|
|
11227
11393
|
if (repos.length === 0) {
|
|
11228
11394
|
if (quiet) return;
|
|
11229
|
-
console.log(
|
|
11395
|
+
console.log(
|
|
11396
|
+
chalk19.red("Not inside a git repository and no git repos found in child directories.")
|
|
11397
|
+
);
|
|
11230
11398
|
throw new Error("__exit__");
|
|
11231
11399
|
}
|
|
11232
11400
|
log2(quiet, chalk19.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
|
|
@@ -11239,12 +11407,19 @@ async function refreshCommand(options) {
|
|
|
11239
11407
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
11240
11408
|
} catch (err) {
|
|
11241
11409
|
if (err instanceof Error && err.message === "__exit__") continue;
|
|
11242
|
-
|
|
11410
|
+
writeRefreshError(err);
|
|
11411
|
+
log2(
|
|
11412
|
+
quiet,
|
|
11413
|
+
chalk19.yellow(
|
|
11414
|
+
`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`
|
|
11415
|
+
)
|
|
11416
|
+
);
|
|
11243
11417
|
}
|
|
11244
11418
|
}
|
|
11245
11419
|
process.chdir(originalDir);
|
|
11246
11420
|
} catch (err) {
|
|
11247
11421
|
if (err instanceof Error && err.message === "__exit__") throw err;
|
|
11422
|
+
writeRefreshError(err);
|
|
11248
11423
|
if (quiet) return;
|
|
11249
11424
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
11250
11425
|
console.log(chalk19.red(`Refresh failed: ${msg}`));
|
|
@@ -11589,10 +11764,10 @@ function writeState2(state) {
|
|
|
11589
11764
|
function resetState() {
|
|
11590
11765
|
writeState2({ ...DEFAULT_STATE });
|
|
11591
11766
|
}
|
|
11592
|
-
var
|
|
11767
|
+
var LOCK_FILE = "finalize.lock";
|
|
11593
11768
|
var LOCK_STALE_MS = 5 * 60 * 1e3;
|
|
11594
11769
|
function lockFilePath() {
|
|
11595
|
-
return path34.join(getLearningDir(),
|
|
11770
|
+
return path34.join(getLearningDir(), LOCK_FILE);
|
|
11596
11771
|
}
|
|
11597
11772
|
function acquireFinalizeLock() {
|
|
11598
11773
|
ensureLearningDir();
|
package/package.json
CHANGED