@rely-ai/caliber 1.35.1 → 1.36.1
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/README.md +2 -2
- package/dist/bin.js +475 -152
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -285,9 +285,9 @@ No API key? No problem. Caliber works with your existing AI tool subscription:
|
|
|
285
285
|
| **Claude Code** (your seat) | `caliber config` → Claude Code | Inherited from Claude Code |
|
|
286
286
|
| **Cursor** (your seat) | `caliber config` → Cursor | Inherited from Cursor |
|
|
287
287
|
| **Anthropic** | `export ANTHROPIC_API_KEY=sk-ant-...` | `claude-sonnet-4-6` |
|
|
288
|
-
| **OpenAI** | `export OPENAI_API_KEY=sk-...` | `gpt-4
|
|
288
|
+
| **OpenAI** | `export OPENAI_API_KEY=sk-...` | `gpt-5.4-mini` |
|
|
289
289
|
| **Vertex AI** | `export VERTEX_PROJECT_ID=my-project` | `claude-sonnet-4-6` |
|
|
290
|
-
| **Custom endpoint** | `OPENAI_API_KEY` + `OPENAI_BASE_URL` | `gpt-4
|
|
290
|
+
| **Custom endpoint** | `OPENAI_API_KEY` + `OPENAI_BASE_URL` | `gpt-5.4-mini` |
|
|
291
291
|
|
|
292
292
|
Override the model for any provider: `export CALIBER_MODEL=<model-name>` or use `caliber config`.
|
|
293
293
|
|
package/dist/bin.js
CHANGED
|
@@ -35,9 +35,9 @@ function getMaxPromptTokens() {
|
|
|
35
35
|
return Math.max(MIN_PROMPT_TOKENS, Math.min(budget, MAX_PROMPT_TOKENS_CAP));
|
|
36
36
|
}
|
|
37
37
|
function loadConfig() {
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
40
|
-
return
|
|
38
|
+
const fileConfig = readConfigFile();
|
|
39
|
+
if (fileConfig) return fileConfig;
|
|
40
|
+
return resolveFromEnv();
|
|
41
41
|
}
|
|
42
42
|
function resolveFromEnv() {
|
|
43
43
|
if (process.env.ANTHROPIC_API_KEY) {
|
|
@@ -130,7 +130,7 @@ var init_config = __esm({
|
|
|
130
130
|
DEFAULT_MODELS = {
|
|
131
131
|
anthropic: "claude-sonnet-4-6",
|
|
132
132
|
vertex: "claude-sonnet-4-6",
|
|
133
|
-
openai: "gpt-4
|
|
133
|
+
openai: "gpt-5.4-mini",
|
|
134
134
|
cursor: "sonnet-4.6",
|
|
135
135
|
"claude-cli": "default"
|
|
136
136
|
};
|
|
@@ -139,8 +139,7 @@ var init_config = __esm({
|
|
|
139
139
|
"claude-opus-4-6": 2e5,
|
|
140
140
|
"claude-haiku-4-5-20251001": 2e5,
|
|
141
141
|
"claude-sonnet-4-5-20250514": 2e5,
|
|
142
|
-
"gpt-4
|
|
143
|
-
"gpt-4.1-mini": 1e6,
|
|
142
|
+
"gpt-5.4-mini": 1e6,
|
|
144
143
|
"gpt-4o": 128e3,
|
|
145
144
|
"gpt-4o-mini": 128e3,
|
|
146
145
|
"sonnet-4.6": 2e5
|
|
@@ -152,7 +151,7 @@ var init_config = __esm({
|
|
|
152
151
|
DEFAULT_FAST_MODELS = {
|
|
153
152
|
anthropic: "claude-haiku-4-5-20251001",
|
|
154
153
|
vertex: "claude-haiku-4-5-20251001",
|
|
155
|
-
openai: "gpt-4
|
|
154
|
+
openai: "gpt-5.4-mini",
|
|
156
155
|
cursor: "gpt-5.3-codex-fast"
|
|
157
156
|
};
|
|
158
157
|
}
|
|
@@ -389,7 +388,10 @@ function getCursorSetupRule() {
|
|
|
389
388
|
function stripManagedBlocks(content) {
|
|
390
389
|
let result = content;
|
|
391
390
|
for (const [start, end] of MANAGED_BLOCK_PAIRS) {
|
|
392
|
-
const regex = new RegExp(
|
|
391
|
+
const regex = new RegExp(
|
|
392
|
+
`\\n?${start.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?${end.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n?`,
|
|
393
|
+
"g"
|
|
394
|
+
);
|
|
393
395
|
result = result.replace(regex, "\n");
|
|
394
396
|
}
|
|
395
397
|
return result.replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
@@ -401,7 +403,7 @@ var init_pre_commit_block = __esm({
|
|
|
401
403
|
init_resolve_caliber();
|
|
402
404
|
BLOCK_START = "<!-- caliber:managed:pre-commit -->";
|
|
403
405
|
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";
|
|
406
|
+
MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md .agents/ .opencode/";
|
|
405
407
|
CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
|
|
406
408
|
LEARNINGS_BLOCK_START = "<!-- caliber:managed:learnings -->";
|
|
407
409
|
LEARNINGS_BLOCK_END = "<!-- /caliber:managed:learnings -->";
|
|
@@ -1100,10 +1102,21 @@ __export(lock_exports, {
|
|
|
1100
1102
|
import fs39 from "fs";
|
|
1101
1103
|
import path32 from "path";
|
|
1102
1104
|
import os8 from "os";
|
|
1105
|
+
import crypto5 from "crypto";
|
|
1106
|
+
function buildLockPath() {
|
|
1107
|
+
const cwd = process.cwd();
|
|
1108
|
+
const hash = crypto5.createHash("md5").update(cwd).digest("hex").slice(0, 8);
|
|
1109
|
+
return path32.join(os8.tmpdir(), `.caliber-${hash}.lock`);
|
|
1110
|
+
}
|
|
1111
|
+
function getLockFile() {
|
|
1112
|
+
if (!_lockPath) _lockPath = buildLockPath();
|
|
1113
|
+
return _lockPath;
|
|
1114
|
+
}
|
|
1103
1115
|
function isCaliberRunning() {
|
|
1104
1116
|
try {
|
|
1105
|
-
|
|
1106
|
-
|
|
1117
|
+
const lockFile = buildLockPath();
|
|
1118
|
+
if (!fs39.existsSync(lockFile)) return false;
|
|
1119
|
+
const raw = fs39.readFileSync(lockFile, "utf-8").trim();
|
|
1107
1120
|
const { pid, ts } = JSON.parse(raw);
|
|
1108
1121
|
if (Date.now() - ts > STALE_MS) return false;
|
|
1109
1122
|
try {
|
|
@@ -1118,22 +1131,23 @@ function isCaliberRunning() {
|
|
|
1118
1131
|
}
|
|
1119
1132
|
function acquireLock() {
|
|
1120
1133
|
try {
|
|
1121
|
-
fs39.writeFileSync(
|
|
1134
|
+
fs39.writeFileSync(getLockFile(), JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
1122
1135
|
} catch {
|
|
1123
1136
|
}
|
|
1124
1137
|
}
|
|
1125
1138
|
function releaseLock() {
|
|
1126
1139
|
try {
|
|
1127
|
-
|
|
1140
|
+
const lockFile = getLockFile();
|
|
1141
|
+
if (fs39.existsSync(lockFile)) fs39.unlinkSync(lockFile);
|
|
1128
1142
|
} catch {
|
|
1129
1143
|
}
|
|
1130
1144
|
}
|
|
1131
|
-
var
|
|
1145
|
+
var STALE_MS, _lockPath;
|
|
1132
1146
|
var init_lock = __esm({
|
|
1133
1147
|
"src/lib/lock.ts"() {
|
|
1134
1148
|
"use strict";
|
|
1135
|
-
LOCK_FILE = path32.join(os8.tmpdir(), ".caliber.lock");
|
|
1136
1149
|
STALE_MS = 10 * 60 * 1e3;
|
|
1150
|
+
_lockPath = null;
|
|
1137
1151
|
}
|
|
1138
1152
|
});
|
|
1139
1153
|
|
|
@@ -1291,9 +1305,30 @@ var LEARNING_ROI_FILE = "roi-stats.json";
|
|
|
1291
1305
|
var PERSONAL_LEARNINGS_FILE = path2.join(AUTH_DIR, "personal-learnings.md");
|
|
1292
1306
|
var LEARNING_FINALIZE_LOG = "finalize.log";
|
|
1293
1307
|
var LEARNING_LAST_ERROR_FILE = "last-error.json";
|
|
1308
|
+
var REFRESH_LAST_ERROR_FILE = path2.join(CALIBER_DIR, "last-refresh-error.json");
|
|
1294
1309
|
var MIN_SESSIONS_FOR_COMPARISON = 3;
|
|
1295
1310
|
|
|
1296
1311
|
// src/fingerprint/existing-config.ts
|
|
1312
|
+
function readSkillsFromDir(skillsDir) {
|
|
1313
|
+
if (!fs2.existsSync(skillsDir)) return void 0;
|
|
1314
|
+
try {
|
|
1315
|
+
const skills = fs2.readdirSync(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).reduce((acc, entry) => {
|
|
1316
|
+
const skillPath = path3.join(skillsDir, entry.name, "SKILL.md");
|
|
1317
|
+
try {
|
|
1318
|
+
acc.push({
|
|
1319
|
+
name: entry.name,
|
|
1320
|
+
filename: "SKILL.md",
|
|
1321
|
+
content: fs2.readFileSync(skillPath, "utf-8")
|
|
1322
|
+
});
|
|
1323
|
+
} catch {
|
|
1324
|
+
}
|
|
1325
|
+
return acc;
|
|
1326
|
+
}, []);
|
|
1327
|
+
return skills.length > 0 ? skills : void 0;
|
|
1328
|
+
} catch {
|
|
1329
|
+
return void 0;
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1297
1332
|
function readExistingConfigs(dir) {
|
|
1298
1333
|
const configs = {};
|
|
1299
1334
|
const readmeMdPath = path3.join(dir, "README.md");
|
|
@@ -1324,7 +1359,10 @@ function readExistingConfigs(dir) {
|
|
|
1324
1359
|
const entryPath = path3.join(skillsDir, entry);
|
|
1325
1360
|
const skillMdPath = path3.join(entryPath, "SKILL.md");
|
|
1326
1361
|
if (fs2.statSync(entryPath).isDirectory() && fs2.existsSync(skillMdPath)) {
|
|
1327
|
-
skills.push({
|
|
1362
|
+
skills.push({
|
|
1363
|
+
filename: `${entry}/SKILL.md`,
|
|
1364
|
+
content: fs2.readFileSync(skillMdPath, "utf-8")
|
|
1365
|
+
});
|
|
1328
1366
|
} else if (entry.endsWith(".md")) {
|
|
1329
1367
|
skills.push({ filename: entry, content: fs2.readFileSync(entryPath, "utf-8") });
|
|
1330
1368
|
}
|
|
@@ -1348,20 +1386,26 @@ function readExistingConfigs(dir) {
|
|
|
1348
1386
|
} catch {
|
|
1349
1387
|
}
|
|
1350
1388
|
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1389
|
+
configs.cursorSkills = readSkillsFromDir(path3.join(dir, ".cursor", "skills"));
|
|
1390
|
+
const copilotPath = path3.join(dir, ".github", "copilot-instructions.md");
|
|
1391
|
+
if (fs2.existsSync(copilotPath)) {
|
|
1392
|
+
configs.copilotInstructions = fs2.readFileSync(copilotPath, "utf-8");
|
|
1393
|
+
}
|
|
1394
|
+
const copilotInstructionsDir = path3.join(dir, ".github", "instructions");
|
|
1395
|
+
if (fs2.existsSync(copilotInstructionsDir)) {
|
|
1353
1396
|
try {
|
|
1354
|
-
const
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
}));
|
|
1397
|
+
const files = fs2.readdirSync(copilotInstructionsDir).filter((f) => f.endsWith(".instructions.md"));
|
|
1398
|
+
if (files.length > 0) {
|
|
1399
|
+
configs.copilotInstructionFiles = files.map((f) => ({
|
|
1400
|
+
filename: f,
|
|
1401
|
+
content: fs2.readFileSync(path3.join(copilotInstructionsDir, f), "utf-8")
|
|
1402
|
+
}));
|
|
1403
|
+
}
|
|
1362
1404
|
} catch {
|
|
1363
1405
|
}
|
|
1364
1406
|
}
|
|
1407
|
+
configs.codexSkills = readSkillsFromDir(path3.join(dir, ".agents", "skills"));
|
|
1408
|
+
configs.opencodeSkills = readSkillsFromDir(path3.join(dir, ".opencode", "skills"));
|
|
1365
1409
|
const mcpJsonPath = path3.join(dir, ".mcp.json");
|
|
1366
1410
|
if (fs2.existsSync(mcpJsonPath)) {
|
|
1367
1411
|
try {
|
|
@@ -2791,13 +2835,7 @@ var KNOWN_MODELS = {
|
|
|
2791
2835
|
"claude-opus-4-6@20250605",
|
|
2792
2836
|
"claude-opus-4-1-20250620"
|
|
2793
2837
|
],
|
|
2794
|
-
openai: [
|
|
2795
|
-
"gpt-4.1",
|
|
2796
|
-
"gpt-4.1-mini",
|
|
2797
|
-
"gpt-4o",
|
|
2798
|
-
"gpt-4o-mini",
|
|
2799
|
-
"o3-mini"
|
|
2800
|
-
],
|
|
2838
|
+
openai: ["gpt-5.4-mini", "gpt-4o", "gpt-4o-mini", "o3-mini"],
|
|
2801
2839
|
cursor: ["auto", "composer-1.5"],
|
|
2802
2840
|
"claude-cli": []
|
|
2803
2841
|
};
|
|
@@ -2805,7 +2843,8 @@ function isModelNotAvailableError(error) {
|
|
|
2805
2843
|
const msg = error.message.toLowerCase();
|
|
2806
2844
|
const status = error.status;
|
|
2807
2845
|
if (status === 404 && msg.includes("model")) return true;
|
|
2808
|
-
if (msg.includes("model") && (msg.includes("not found") || msg.includes("not_found")))
|
|
2846
|
+
if (msg.includes("model") && (msg.includes("not found") || msg.includes("not_found")))
|
|
2847
|
+
return true;
|
|
2809
2848
|
if (msg.includes("model") && msg.includes("not available")) return true;
|
|
2810
2849
|
if (msg.includes("model") && msg.includes("does not exist")) return true;
|
|
2811
2850
|
if (msg.includes("publisher model")) return true;
|
|
@@ -2831,12 +2870,18 @@ function filterRelevantModels(models, provider) {
|
|
|
2831
2870
|
async function handleModelNotAvailable(failedModel, provider, config) {
|
|
2832
2871
|
if (!process.stdin.isTTY) {
|
|
2833
2872
|
console.error(
|
|
2834
|
-
chalk.red(
|
|
2873
|
+
chalk.red(
|
|
2874
|
+
`Model "${failedModel}" is not available. Run \`${resolveCaliber()} config\` to select a different model.`
|
|
2875
|
+
)
|
|
2835
2876
|
);
|
|
2836
2877
|
return null;
|
|
2837
2878
|
}
|
|
2838
|
-
console.log(
|
|
2839
|
-
|
|
2879
|
+
console.log(
|
|
2880
|
+
chalk.yellow(
|
|
2881
|
+
`
|
|
2882
|
+
\u26A0 Model "${failedModel}" is not available on your ${config.provider} deployment.`
|
|
2883
|
+
)
|
|
2884
|
+
);
|
|
2840
2885
|
let models = [];
|
|
2841
2886
|
if (provider.listModels) {
|
|
2842
2887
|
try {
|
|
@@ -2850,7 +2895,11 @@ async function handleModelNotAvailable(failedModel, provider, config) {
|
|
|
2850
2895
|
}
|
|
2851
2896
|
models = models.filter((m) => m !== failedModel);
|
|
2852
2897
|
if (models.length === 0) {
|
|
2853
|
-
console.log(
|
|
2898
|
+
console.log(
|
|
2899
|
+
chalk.red(
|
|
2900
|
+
` No alternative models found. Run \`${resolveCaliber()} config\` to configure manually.`
|
|
2901
|
+
)
|
|
2902
|
+
);
|
|
2854
2903
|
return null;
|
|
2855
2904
|
}
|
|
2856
2905
|
console.log("");
|
|
@@ -3325,6 +3374,11 @@ Quality constraints (the output is scored deterministically):
|
|
|
3325
3374
|
- ONLY reference file paths that exist in the provided file tree \u2014 do NOT invent paths
|
|
3326
3375
|
- Preserve the existing structure (headings, bullet style, formatting)
|
|
3327
3376
|
|
|
3377
|
+
Cross-agent sync:
|
|
3378
|
+
- When a code change affects documentation, update ALL provided platform configs together.
|
|
3379
|
+
- A renamed command, moved file, or changed convention must be reflected in every config (CLAUDE.md, AGENTS.md, copilot instructions, skills across all platforms).
|
|
3380
|
+
- Cross-agent consistency is critical \u2014 all agents working on this repo must have the same, accurate context.
|
|
3381
|
+
|
|
3328
3382
|
Managed content:
|
|
3329
3383
|
- Keep managed blocks (<!-- caliber:managed --> ... <!-- /caliber:managed -->) intact
|
|
3330
3384
|
- Do NOT modify CALIBER_LEARNINGS.md \u2014 it is managed separately
|
|
@@ -3338,7 +3392,6 @@ Return a JSON object with this exact shape:
|
|
|
3338
3392
|
"readmeMd": "<updated content or null>",
|
|
3339
3393
|
"cursorrules": "<updated content or null>",
|
|
3340
3394
|
"cursorRules": [{"filename": "name.mdc", "content": "..."}] or null,
|
|
3341
|
-
"claudeSkills": [{"filename": "name.md", "content": "..."}] or null,
|
|
3342
3395
|
"copilotInstructions": "<updated content or null>",
|
|
3343
3396
|
"copilotInstructionFiles": [{"filename": "name.instructions.md", "content": "..."}] or null
|
|
3344
3397
|
},
|
|
@@ -3930,7 +3983,7 @@ var STOP_HOOK_SCRIPT_CONTENT = `#!/bin/sh
|
|
|
3930
3983
|
if grep -q "caliber" .git/hooks/pre-commit 2>/dev/null; then
|
|
3931
3984
|
exit 0
|
|
3932
3985
|
fi
|
|
3933
|
-
FLAG="/tmp/caliber-nudge-$(echo "$PWD" | shasum | cut -c1-8)"
|
|
3986
|
+
FLAG="/tmp/caliber-nudge-$(echo "$PWD" | (shasum 2>/dev/null || sha1sum 2>/dev/null || md5sum 2>/dev/null || cksum) | cut -c1-8)"
|
|
3934
3987
|
find /tmp -maxdepth 1 -name "caliber-nudge-*" -mmin +120 -delete 2>/dev/null
|
|
3935
3988
|
if [ -f "$FLAG" ]; then
|
|
3936
3989
|
exit 0
|
|
@@ -3961,11 +4014,13 @@ function installStopHook() {
|
|
|
3961
4014
|
}
|
|
3962
4015
|
settings.hooks.Stop.push({
|
|
3963
4016
|
matcher: "",
|
|
3964
|
-
hooks: [
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
4017
|
+
hooks: [
|
|
4018
|
+
{
|
|
4019
|
+
type: "command",
|
|
4020
|
+
command: STOP_HOOK_SCRIPT_PATH,
|
|
4021
|
+
description: STOP_HOOK_DESCRIPTION
|
|
4022
|
+
}
|
|
4023
|
+
]
|
|
3969
4024
|
});
|
|
3970
4025
|
writeSettings(settings);
|
|
3971
4026
|
return { installed: true, alreadyInstalled: false };
|
|
@@ -3997,16 +4052,20 @@ function getPrecommitBlock() {
|
|
|
3997
4052
|
const invoke = npx ? bin : `"${bin}"`;
|
|
3998
4053
|
return `${PRECOMMIT_START}
|
|
3999
4054
|
if ${guard}; then
|
|
4055
|
+
mkdir -p .caliber
|
|
4000
4056
|
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
|
|
4057
|
+
${invoke} refresh --quiet 2>.caliber/refresh-hook.log || true
|
|
4058
|
+
${invoke} learn finalize 2>>.caliber/refresh-hook.log || true
|
|
4059
|
+
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
4060
|
fi
|
|
4005
4061
|
${PRECOMMIT_END}`;
|
|
4006
4062
|
}
|
|
4007
4063
|
function getGitHooksDir() {
|
|
4008
4064
|
try {
|
|
4009
|
-
const gitDir = execSync8("git rev-parse --git-dir", {
|
|
4065
|
+
const gitDir = execSync8("git rev-parse --git-dir", {
|
|
4066
|
+
encoding: "utf-8",
|
|
4067
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4068
|
+
}).trim();
|
|
4010
4069
|
return path10.join(gitDir, "hooks");
|
|
4011
4070
|
} catch {
|
|
4012
4071
|
return null;
|
|
@@ -5294,7 +5353,10 @@ import fs14 from "fs";
|
|
|
5294
5353
|
import path14 from "path";
|
|
5295
5354
|
function writeCodexConfig(config) {
|
|
5296
5355
|
const written = [];
|
|
5297
|
-
fs14.writeFileSync(
|
|
5356
|
+
fs14.writeFileSync(
|
|
5357
|
+
"AGENTS.md",
|
|
5358
|
+
appendLearningsBlock(appendPreCommitBlock(config.agentsMd, "codex"))
|
|
5359
|
+
);
|
|
5298
5360
|
written.push("AGENTS.md");
|
|
5299
5361
|
if (config.skills?.length) {
|
|
5300
5362
|
for (const skill of config.skills) {
|
|
@@ -5323,7 +5385,10 @@ function writeGithubCopilotConfig(config) {
|
|
|
5323
5385
|
const written = [];
|
|
5324
5386
|
if (config.instructions) {
|
|
5325
5387
|
fs15.mkdirSync(".github", { recursive: true });
|
|
5326
|
-
fs15.writeFileSync(
|
|
5388
|
+
fs15.writeFileSync(
|
|
5389
|
+
path15.join(".github", "copilot-instructions.md"),
|
|
5390
|
+
appendLearningsBlock(appendPreCommitBlock(config.instructions, "copilot"))
|
|
5391
|
+
);
|
|
5327
5392
|
written.push(".github/copilot-instructions.md");
|
|
5328
5393
|
}
|
|
5329
5394
|
if (config.instructionFiles?.length) {
|
|
@@ -5345,7 +5410,10 @@ import path17 from "path";
|
|
|
5345
5410
|
function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
|
|
5346
5411
|
const written = [];
|
|
5347
5412
|
if (!agentsMdAlreadyWritten) {
|
|
5348
|
-
fs17.writeFileSync(
|
|
5413
|
+
fs17.writeFileSync(
|
|
5414
|
+
"AGENTS.md",
|
|
5415
|
+
appendLearningsBlock(appendPreCommitBlock(config.agentsMd, "codex"))
|
|
5416
|
+
);
|
|
5349
5417
|
written.push("AGENTS.md");
|
|
5350
5418
|
}
|
|
5351
5419
|
if (config.skills?.length) {
|
|
@@ -6224,14 +6292,17 @@ function checkExistence(dir) {
|
|
|
6224
6292
|
const opencodeSkills = countFiles(join3(dir, ".opencode", "skills"), /SKILL\.md$/);
|
|
6225
6293
|
const skillCount = claudeSkills.length + codexSkills.length + opencodeSkills.length;
|
|
6226
6294
|
const skillBase = skillCount >= 1 ? POINTS_SKILLS_EXIST : 0;
|
|
6227
|
-
const skillBonus = Math.min(
|
|
6295
|
+
const skillBonus = Math.min(
|
|
6296
|
+
(skillCount - 1) * POINTS_SKILLS_BONUS_PER_EXTRA,
|
|
6297
|
+
POINTS_SKILLS_BONUS_CAP
|
|
6298
|
+
);
|
|
6228
6299
|
const skillPoints = skillCount >= 1 ? skillBase + Math.max(0, skillBonus) : 0;
|
|
6229
6300
|
const maxSkillPoints = POINTS_SKILLS_EXIST + POINTS_SKILLS_BONUS_CAP;
|
|
6230
6301
|
checks.push({
|
|
6231
6302
|
id: "skills_exist",
|
|
6232
6303
|
name: "Skills configured",
|
|
6233
6304
|
category: "existence",
|
|
6234
|
-
maxPoints: maxSkillPoints,
|
|
6305
|
+
maxPoints: skillCount >= 1 ? maxSkillPoints : 0,
|
|
6235
6306
|
earnedPoints: Math.min(skillPoints, maxSkillPoints),
|
|
6236
6307
|
passed: skillCount >= 1,
|
|
6237
6308
|
detail: skillCount === 0 ? "No skills found" : `${skillCount} skill${skillCount === 1 ? "" : "s"} found`,
|
|
@@ -6264,7 +6335,7 @@ function checkExistence(dir) {
|
|
|
6264
6335
|
id: "mcp_servers",
|
|
6265
6336
|
name: "MCP servers configured",
|
|
6266
6337
|
category: "existence",
|
|
6267
|
-
maxPoints: POINTS_MCP_SERVERS,
|
|
6338
|
+
maxPoints: mcp.count >= 1 ? POINTS_MCP_SERVERS : 0,
|
|
6268
6339
|
earnedPoints: mcp.count >= 1 ? POINTS_MCP_SERVERS : 0,
|
|
6269
6340
|
passed: mcp.count >= 1,
|
|
6270
6341
|
detail: mcp.count > 0 ? `${mcp.count} server${mcp.count === 1 ? "" : "s"} in ${mcp.sources.join(", ")}` : "No MCP servers configured",
|
|
@@ -6799,7 +6870,11 @@ init_resolve_caliber();
|
|
|
6799
6870
|
init_pre_commit_block();
|
|
6800
6871
|
function hasPreCommitHook(dir) {
|
|
6801
6872
|
try {
|
|
6802
|
-
const gitDir = execSync12("git rev-parse --git-dir", {
|
|
6873
|
+
const gitDir = execSync12("git rev-parse --git-dir", {
|
|
6874
|
+
cwd: dir,
|
|
6875
|
+
encoding: "utf-8",
|
|
6876
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
6877
|
+
}).trim();
|
|
6803
6878
|
const hookPath = join7(gitDir, "hooks", "pre-commit");
|
|
6804
6879
|
const content = readFileOrNull(hookPath);
|
|
6805
6880
|
return content ? content.includes("caliber") : false;
|
|
@@ -6890,9 +6965,9 @@ function checkBonus(dir) {
|
|
|
6890
6965
|
id: "open_skills_format",
|
|
6891
6966
|
name: "Skills use OpenSkills format",
|
|
6892
6967
|
category: "bonus",
|
|
6893
|
-
maxPoints: POINTS_OPEN_SKILLS_FORMAT,
|
|
6968
|
+
maxPoints: totalSkillFiles > 0 ? POINTS_OPEN_SKILLS_FORMAT : 0,
|
|
6894
6969
|
earnedPoints: allOpenSkills ? POINTS_OPEN_SKILLS_FORMAT : 0,
|
|
6895
|
-
passed: allOpenSkills,
|
|
6970
|
+
passed: allOpenSkills || totalSkillFiles === 0,
|
|
6896
6971
|
detail: totalSkillFiles === 0 ? "No skills to check" : allOpenSkills ? `All ${totalSkillFiles} skill${totalSkillFiles === 1 ? "" : "s"} use SKILL.md with frontmatter` : `${openSkillsCount}/${totalSkillFiles} use OpenSkills format`,
|
|
6897
6972
|
suggestion: totalSkillFiles > 0 && !allOpenSkills ? "Migrate skills to .claude/skills/{name}/SKILL.md with YAML frontmatter" : void 0,
|
|
6898
6973
|
fix: totalSkillFiles > 0 && !allOpenSkills ? {
|
|
@@ -9686,27 +9761,47 @@ async function initCommand(options) {
|
|
|
9686
9761
|
const bin = resolveCaliber();
|
|
9687
9762
|
const firstRun = isFirstRun(process.cwd());
|
|
9688
9763
|
if (firstRun) {
|
|
9689
|
-
console.log(
|
|
9764
|
+
console.log(
|
|
9765
|
+
brand.bold(`
|
|
9690
9766
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
9691
9767
|
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
9692
9768
|
\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
9693
9769
|
\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
9694
9770
|
\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
9695
9771
|
\u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
|
|
9696
|
-
`)
|
|
9772
|
+
`)
|
|
9773
|
+
);
|
|
9697
9774
|
console.log(chalk14.dim(" Keep your AI agent configs in sync \u2014 automatically."));
|
|
9698
9775
|
console.log(chalk14.dim(" Works across Claude Code, Cursor, Codex, and GitHub Copilot.\n"));
|
|
9699
|
-
console.log(title.bold("
|
|
9700
|
-
console.log(
|
|
9701
|
-
|
|
9702
|
-
|
|
9776
|
+
console.log(title.bold(" What this does:\n"));
|
|
9777
|
+
console.log(
|
|
9778
|
+
chalk14.dim(" Caliber reads your project structure (file tree, package.json, etc.)")
|
|
9779
|
+
);
|
|
9780
|
+
console.log(chalk14.dim(" and generates agent config files (CLAUDE.md, .cursor/rules/, etc.)."));
|
|
9781
|
+
console.log(chalk14.dim(" You review all changes before anything is written to disk.\n"));
|
|
9782
|
+
console.log(title.bold(" Steps:\n"));
|
|
9783
|
+
console.log(
|
|
9784
|
+
chalk14.dim(" 1. Connect Pick your LLM provider (or use your existing subscription)")
|
|
9785
|
+
);
|
|
9786
|
+
console.log(
|
|
9787
|
+
chalk14.dim(" 2. Build Scan project, generate configs, install pre-commit sync")
|
|
9788
|
+
);
|
|
9789
|
+
console.log(
|
|
9790
|
+
chalk14.dim(" 3. Review See exactly what changed \u2014 accept, refine, or decline\n")
|
|
9791
|
+
);
|
|
9703
9792
|
} else {
|
|
9704
9793
|
console.log(brand.bold("\n CALIBER") + chalk14.dim(" \u2014 setting up continuous sync\n"));
|
|
9705
9794
|
}
|
|
9706
9795
|
const platforms = detectPlatforms();
|
|
9707
9796
|
if (!platforms.claude && !platforms.cursor && !platforms.codex && !platforms.opencode) {
|
|
9708
|
-
console.log(
|
|
9709
|
-
|
|
9797
|
+
console.log(
|
|
9798
|
+
chalk14.yellow(" \u26A0 No supported AI platforms detected (Claude, Cursor, Codex, OpenCode).")
|
|
9799
|
+
);
|
|
9800
|
+
console.log(
|
|
9801
|
+
chalk14.yellow(
|
|
9802
|
+
" Caliber will still generate config files, but they won't be auto-installed.\n"
|
|
9803
|
+
)
|
|
9804
|
+
);
|
|
9710
9805
|
}
|
|
9711
9806
|
const report = options.debugReport ? new DebugReport() : null;
|
|
9712
9807
|
console.log(title.bold(" Step 1/3 \u2014 Connect\n"));
|
|
@@ -9761,9 +9856,12 @@ async function initCommand(options) {
|
|
|
9761
9856
|
console.log(chalk14.dim(modelLine + "\n"));
|
|
9762
9857
|
if (report) {
|
|
9763
9858
|
report.markStep("Provider connection");
|
|
9764
|
-
report.addSection(
|
|
9859
|
+
report.addSection(
|
|
9860
|
+
"LLM Provider",
|
|
9861
|
+
`- **Provider**: ${config.provider}
|
|
9765
9862
|
- **Model**: ${displayModel}
|
|
9766
|
-
- **Fast model**: ${fastModel || "none"}`
|
|
9863
|
+
- **Fast model**: ${fastModel || "none"}`
|
|
9864
|
+
);
|
|
9767
9865
|
}
|
|
9768
9866
|
await validateModel({ fast: true });
|
|
9769
9867
|
let targetAgent;
|
|
@@ -9799,9 +9897,12 @@ async function initCommand(options) {
|
|
|
9799
9897
|
console.log(` ${chalk14.green("\u2713")} Onboarding hook \u2014 nudges new team members to set up`);
|
|
9800
9898
|
const { ensureBuiltinSkills: ensureBuiltinSkills2 } = await Promise.resolve().then(() => (init_builtin_skills(), builtin_skills_exports));
|
|
9801
9899
|
for (const agent of targetAgent) {
|
|
9802
|
-
if (agent === "claude" && !fs34.existsSync(".claude"))
|
|
9803
|
-
|
|
9804
|
-
if (agent === "
|
|
9900
|
+
if (agent === "claude" && !fs34.existsSync(".claude"))
|
|
9901
|
+
fs34.mkdirSync(".claude", { recursive: true });
|
|
9902
|
+
if (agent === "cursor" && !fs34.existsSync(".cursor"))
|
|
9903
|
+
fs34.mkdirSync(".cursor", { recursive: true });
|
|
9904
|
+
if (agent === "codex" && !fs34.existsSync(".agents"))
|
|
9905
|
+
fs34.mkdirSync(".agents", { recursive: true });
|
|
9805
9906
|
}
|
|
9806
9907
|
const skillsWritten = ensureBuiltinSkills2();
|
|
9807
9908
|
if (skillsWritten.length > 0) {
|
|
@@ -9819,11 +9920,16 @@ async function initCommand(options) {
|
|
|
9819
9920
|
log(options.verbose, `Baseline score: ${baselineScore.score}/100`);
|
|
9820
9921
|
if (report) {
|
|
9821
9922
|
report.markStep("Baseline scoring");
|
|
9822
|
-
report.addSection(
|
|
9923
|
+
report.addSection(
|
|
9924
|
+
"Scoring: Baseline",
|
|
9925
|
+
`**Score**: ${baselineScore.score}/100
|
|
9823
9926
|
|
|
9824
9927
|
| Check | Passed | Points | Max |
|
|
9825
9928
|
|-------|--------|--------|-----|
|
|
9826
|
-
` + baselineScore.checks.map(
|
|
9929
|
+
` + baselineScore.checks.map(
|
|
9930
|
+
(c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`
|
|
9931
|
+
).join("\n")
|
|
9932
|
+
);
|
|
9827
9933
|
report.addSection("Generation: Target Agents", targetAgent.join(", "));
|
|
9828
9934
|
}
|
|
9829
9935
|
const hasExistingConfig = !!(baselineScore.checks.some((c) => c.id === "claude_md_exists" && c.passed) || baselineScore.checks.some((c) => c.id === "cursorrules_exists" && c.passed));
|
|
@@ -9842,8 +9948,19 @@ async function initCommand(options) {
|
|
|
9842
9948
|
if (hasExistingConfig && baselineScore.score === 100 && !options.force) {
|
|
9843
9949
|
skipGeneration = true;
|
|
9844
9950
|
} else if (hasExistingConfig && !options.force && !options.autoApprove) {
|
|
9845
|
-
|
|
9951
|
+
const topGains = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0).sort((a, b) => b.maxPoints - b.earnedPoints - (a.maxPoints - a.earnedPoints)).slice(0, 3);
|
|
9952
|
+
console.log(chalk14.dim(` Config score: ${baselineScore.score}/100
|
|
9846
9953
|
`));
|
|
9954
|
+
if (topGains.length > 0) {
|
|
9955
|
+
console.log(chalk14.dim(" Top improvements Caliber can make:"));
|
|
9956
|
+
for (const c of topGains) {
|
|
9957
|
+
const pts = c.maxPoints - c.earnedPoints;
|
|
9958
|
+
console.log(
|
|
9959
|
+
chalk14.dim(` +${pts} pts`) + chalk14.white(` ${c.name}`) + (c.suggestion ? chalk14.gray(` \u2014 ${c.suggestion}`) : "")
|
|
9960
|
+
);
|
|
9961
|
+
}
|
|
9962
|
+
console.log("");
|
|
9963
|
+
}
|
|
9847
9964
|
const improveAnswer = await confirm2({ message: "Improve your existing configs?" });
|
|
9848
9965
|
skipGeneration = !improveAnswer;
|
|
9849
9966
|
}
|
|
@@ -9873,7 +9990,12 @@ async function initCommand(options) {
|
|
|
9873
9990
|
if (targetAgent.includes("cursor")) {
|
|
9874
9991
|
const rulesDir = path28.join(".cursor", "rules");
|
|
9875
9992
|
if (!fs34.existsSync(rulesDir)) fs34.mkdirSync(rulesDir, { recursive: true });
|
|
9876
|
-
for (const rule of [
|
|
9993
|
+
for (const rule of [
|
|
9994
|
+
getCursorPreCommitRule2(),
|
|
9995
|
+
getCursorLearningsRule2(),
|
|
9996
|
+
getCursorSyncRule2(),
|
|
9997
|
+
getCursorSetupRule2()
|
|
9998
|
+
]) {
|
|
9877
9999
|
fs34.writeFileSync(path28.join(rulesDir, rule.filename), rule.content);
|
|
9878
10000
|
}
|
|
9879
10001
|
console.log(` ${chalk14.green("\u2713")} Cursor rules \u2014 added Caliber sync rules`);
|
|
@@ -9905,7 +10027,9 @@ async function initCommand(options) {
|
|
|
9905
10027
|
trackInitCompleted("sync-only", baselineScore.score);
|
|
9906
10028
|
console.log(chalk14.bold.green("\n Caliber sync is set up!\n"));
|
|
9907
10029
|
console.log(chalk14.dim(" Your agent configs will sync automatically on every commit."));
|
|
9908
|
-
console.log(
|
|
10030
|
+
console.log(
|
|
10031
|
+
chalk14.dim(" Run ") + title(`${bin} init --force`) + chalk14.dim(" anytime to generate or improve configs.\n")
|
|
10032
|
+
);
|
|
9909
10033
|
return;
|
|
9910
10034
|
}
|
|
9911
10035
|
const genModelInfo = fastModel ? ` Using ${displayModel} for docs, ${fastModel} for skills` : ` Using ${displayModel}`;
|
|
@@ -9923,8 +10047,14 @@ async function initCommand(options) {
|
|
|
9923
10047
|
const TASK_STACK = display.add("Detecting project stack", { pipelineLabel: "Scan" });
|
|
9924
10048
|
const TASK_CONFIG = display.add("Generating configs", { depth: 1, pipelineLabel: "Generate" });
|
|
9925
10049
|
const TASK_SKILLS_GEN = display.add("Generating skills", { depth: 2, pipelineLabel: "Skills" });
|
|
9926
|
-
const TASK_SKILLS_SEARCH = display.add("Searching community skills", {
|
|
9927
|
-
|
|
10050
|
+
const TASK_SKILLS_SEARCH = display.add("Searching community skills", {
|
|
10051
|
+
depth: 1,
|
|
10052
|
+
pipelineLabel: "Search",
|
|
10053
|
+
pipelineRow: 1
|
|
10054
|
+
});
|
|
10055
|
+
const TASK_SCORE_REFINE = display.add("Validating & refining config", {
|
|
10056
|
+
pipelineLabel: "Validate"
|
|
10057
|
+
});
|
|
9928
10058
|
display.start();
|
|
9929
10059
|
display.enableWaitingContent();
|
|
9930
10060
|
try {
|
|
@@ -9934,21 +10064,39 @@ async function initCommand(options) {
|
|
|
9934
10064
|
const stackSummary = stackParts.join(", ") || "no languages";
|
|
9935
10065
|
const largeRepoNote = fingerprint.fileTree.length > 5e3 ? ` (${fingerprint.fileTree.length.toLocaleString()} files, smart sampling active)` : "";
|
|
9936
10066
|
display.update(TASK_STACK, "done", stackSummary + largeRepoNote);
|
|
9937
|
-
trackInitProjectDiscovered(
|
|
9938
|
-
|
|
10067
|
+
trackInitProjectDiscovered(
|
|
10068
|
+
fingerprint.languages.length,
|
|
10069
|
+
fingerprint.frameworks.length,
|
|
10070
|
+
fingerprint.fileTree.length
|
|
10071
|
+
);
|
|
10072
|
+
log(
|
|
10073
|
+
options.verbose,
|
|
10074
|
+
`Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`
|
|
10075
|
+
);
|
|
9939
10076
|
const cliSources = options.source || [];
|
|
9940
10077
|
const workspaces = getDetectedWorkspaces(process.cwd());
|
|
9941
10078
|
const sources2 = resolveAllSources(process.cwd(), cliSources, workspaces);
|
|
9942
10079
|
if (sources2.length > 0) {
|
|
9943
10080
|
fingerprint.sources = sources2;
|
|
9944
|
-
log(
|
|
10081
|
+
log(
|
|
10082
|
+
options.verbose,
|
|
10083
|
+
`Sources: ${sources2.length} resolved (${sources2.map((s) => s.name).join(", ")})`
|
|
10084
|
+
);
|
|
9945
10085
|
}
|
|
9946
10086
|
if (report) {
|
|
9947
|
-
report.addJson("Fingerprint: Git", {
|
|
10087
|
+
report.addJson("Fingerprint: Git", {
|
|
10088
|
+
remote: fingerprint.gitRemoteUrl,
|
|
10089
|
+
packageName: fingerprint.packageName
|
|
10090
|
+
});
|
|
9948
10091
|
report.addCodeBlock("Fingerprint: File Tree", fingerprint.fileTree.join("\n"));
|
|
9949
|
-
report.addJson("Fingerprint: Detected Stack", {
|
|
10092
|
+
report.addJson("Fingerprint: Detected Stack", {
|
|
10093
|
+
languages: fingerprint.languages,
|
|
10094
|
+
frameworks: fingerprint.frameworks,
|
|
10095
|
+
tools: fingerprint.tools
|
|
10096
|
+
});
|
|
9950
10097
|
report.addJson("Fingerprint: Existing Configs", fingerprint.existingConfigs);
|
|
9951
|
-
if (fingerprint.codeAnalysis)
|
|
10098
|
+
if (fingerprint.codeAnalysis)
|
|
10099
|
+
report.addJson("Fingerprint: Code Analysis", fingerprint.codeAnalysis);
|
|
9952
10100
|
}
|
|
9953
10101
|
const isEmpty = fingerprint.fileTree.length < 3;
|
|
9954
10102
|
if (isEmpty) {
|
|
@@ -9979,13 +10127,26 @@ async function initCommand(options) {
|
|
|
9979
10127
|
let passingChecks;
|
|
9980
10128
|
let currentScore;
|
|
9981
10129
|
if (hasExistingConfig && localBaseline.score >= 95 && !options.force) {
|
|
9982
|
-
const currentLlmFixable = localBaseline.checks.filter(
|
|
9983
|
-
|
|
10130
|
+
const currentLlmFixable = localBaseline.checks.filter(
|
|
10131
|
+
(c) => !c.passed && c.maxPoints > 0 && !NON_LLM_CHECKS.has(c.id)
|
|
10132
|
+
);
|
|
10133
|
+
failingChecks = currentLlmFixable.map((c) => ({
|
|
10134
|
+
name: c.name,
|
|
10135
|
+
suggestion: c.suggestion,
|
|
10136
|
+
fix: c.fix
|
|
10137
|
+
}));
|
|
9984
10138
|
passingChecks = localBaseline.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
|
|
9985
10139
|
currentScore = localBaseline.score;
|
|
9986
10140
|
}
|
|
9987
10141
|
if (report) {
|
|
9988
|
-
const fullPrompt = buildGeneratePrompt(
|
|
10142
|
+
const fullPrompt = buildGeneratePrompt(
|
|
10143
|
+
fingerprint,
|
|
10144
|
+
targetAgent,
|
|
10145
|
+
fingerprint.description,
|
|
10146
|
+
failingChecks,
|
|
10147
|
+
currentScore,
|
|
10148
|
+
passingChecks
|
|
10149
|
+
);
|
|
9989
10150
|
report.addCodeBlock("Generation: Full LLM Prompt", fullPrompt);
|
|
9990
10151
|
}
|
|
9991
10152
|
const result = await generateSetup(
|
|
@@ -10105,7 +10266,10 @@ async function initCommand(options) {
|
|
|
10105
10266
|
if (rawOutput) report.addCodeBlock("Generation: Raw LLM Response", rawOutput);
|
|
10106
10267
|
report.addJson("Generation: Parsed Config", generatedSetup);
|
|
10107
10268
|
}
|
|
10108
|
-
log(
|
|
10269
|
+
log(
|
|
10270
|
+
options.verbose,
|
|
10271
|
+
`Generation completed: ${elapsedMs}ms, stopReason: ${genStopReason || "end_turn"}`
|
|
10272
|
+
);
|
|
10109
10273
|
console.log(title.bold(" Step 3/3 \u2014 Done\n"));
|
|
10110
10274
|
const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
|
|
10111
10275
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
@@ -10117,10 +10281,18 @@ async function initCommand(options) {
|
|
|
10117
10281
|
}
|
|
10118
10282
|
console.log("");
|
|
10119
10283
|
}
|
|
10120
|
-
console.log(
|
|
10284
|
+
console.log(
|
|
10285
|
+
chalk14.dim(
|
|
10286
|
+
` ${chalk14.green(`${staged.newFiles} new`)} / ${chalk14.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}`
|
|
10287
|
+
)
|
|
10288
|
+
);
|
|
10121
10289
|
if (skillSearchResult.results.length > 0) {
|
|
10122
|
-
console.log(
|
|
10123
|
-
|
|
10290
|
+
console.log(
|
|
10291
|
+
chalk14.dim(
|
|
10292
|
+
` ${chalk14.cyan(`${skillSearchResult.results.length}`)} community skills available to install
|
|
10293
|
+
`
|
|
10294
|
+
)
|
|
10295
|
+
);
|
|
10124
10296
|
} else {
|
|
10125
10297
|
console.log("");
|
|
10126
10298
|
}
|
|
@@ -10156,8 +10328,12 @@ async function initCommand(options) {
|
|
|
10156
10328
|
}
|
|
10157
10329
|
const updatedFiles = collectSetupFiles(generatedSetup, targetAgent);
|
|
10158
10330
|
const restaged = stageFiles(updatedFiles, process.cwd());
|
|
10159
|
-
console.log(
|
|
10160
|
-
|
|
10331
|
+
console.log(
|
|
10332
|
+
chalk14.dim(
|
|
10333
|
+
` ${chalk14.green(`${restaged.newFiles} new`)} / ${chalk14.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
|
|
10334
|
+
`
|
|
10335
|
+
)
|
|
10336
|
+
);
|
|
10161
10337
|
printSetupSummary(generatedSetup);
|
|
10162
10338
|
const { openReview: openRev } = await Promise.resolve().then(() => (init_review(), review_exports));
|
|
10163
10339
|
await openRev("terminal", restaged.stagedFiles);
|
|
@@ -10183,7 +10359,8 @@ async function initCommand(options) {
|
|
|
10183
10359
|
const agentRefs = [];
|
|
10184
10360
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
10185
10361
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
10186
|
-
if (agentRefs.length === 0)
|
|
10362
|
+
if (agentRefs.length === 0)
|
|
10363
|
+
agentRefs.push("See CLAUDE.md and .cursor/rules/ for agent configurations.");
|
|
10187
10364
|
const stubContent = `# AGENTS.md
|
|
10188
10365
|
|
|
10189
10366
|
This project uses AI coding agents configured by [Caliber](https://github.com/caliber-ai-org/ai-setup).
|
|
@@ -10230,24 +10407,39 @@ ${agentRefs.join(" ")}
|
|
|
10230
10407
|
if (afterScore.score < baselineScore.score) {
|
|
10231
10408
|
trackInitScoreRegression(baselineScore.score, afterScore.score);
|
|
10232
10409
|
console.log("");
|
|
10233
|
-
console.log(
|
|
10410
|
+
console.log(
|
|
10411
|
+
chalk14.yellow(
|
|
10412
|
+
` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`
|
|
10413
|
+
)
|
|
10414
|
+
);
|
|
10234
10415
|
try {
|
|
10235
10416
|
const { restored, removed } = undoSetup();
|
|
10236
10417
|
if (restored.length > 0 || removed.length > 0) {
|
|
10237
|
-
console.log(
|
|
10418
|
+
console.log(
|
|
10419
|
+
chalk14.dim(
|
|
10420
|
+
` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`
|
|
10421
|
+
)
|
|
10422
|
+
);
|
|
10238
10423
|
}
|
|
10239
10424
|
} catch {
|
|
10240
10425
|
}
|
|
10241
|
-
console.log(
|
|
10426
|
+
console.log(
|
|
10427
|
+
chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to override.\n")
|
|
10428
|
+
);
|
|
10242
10429
|
return;
|
|
10243
10430
|
}
|
|
10244
10431
|
if (report) {
|
|
10245
10432
|
report.markStep("Post-write scoring");
|
|
10246
|
-
report.addSection(
|
|
10433
|
+
report.addSection(
|
|
10434
|
+
"Scoring: Post-Write",
|
|
10435
|
+
`**Score**: ${afterScore.score}/100 (delta: ${afterScore.score - baselineScore.score >= 0 ? "+" : ""}${afterScore.score - baselineScore.score})
|
|
10247
10436
|
|
|
10248
10437
|
| Check | Passed | Points | Max |
|
|
10249
10438
|
|-------|--------|--------|-----|
|
|
10250
|
-
` + afterScore.checks.map(
|
|
10439
|
+
` + afterScore.checks.map(
|
|
10440
|
+
(c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`
|
|
10441
|
+
).join("\n")
|
|
10442
|
+
);
|
|
10251
10443
|
}
|
|
10252
10444
|
recordScore(afterScore, "init");
|
|
10253
10445
|
trackInitCompleted("full-generation", afterScore.score);
|
|
@@ -10255,7 +10447,10 @@ ${agentRefs.join(" ")}
|
|
|
10255
10447
|
if (options.verbose) {
|
|
10256
10448
|
log(options.verbose, `Final score: ${afterScore.score}/100`);
|
|
10257
10449
|
for (const c of afterScore.checks.filter((ch) => !ch.passed)) {
|
|
10258
|
-
log(
|
|
10450
|
+
log(
|
|
10451
|
+
options.verbose,
|
|
10452
|
+
` Still failing: ${c.name} (${c.earnedPoints}/${c.maxPoints})${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`
|
|
10453
|
+
);
|
|
10259
10454
|
}
|
|
10260
10455
|
}
|
|
10261
10456
|
let communitySkillsInstalled = 0;
|
|
@@ -10272,14 +10467,20 @@ ${agentRefs.join(" ")}
|
|
|
10272
10467
|
const done = chalk14.green("\u2713");
|
|
10273
10468
|
console.log(chalk14.bold.green("\n Caliber is set up!\n"));
|
|
10274
10469
|
console.log(chalk14.bold(" What's configured:\n"));
|
|
10275
|
-
console.log(
|
|
10470
|
+
console.log(
|
|
10471
|
+
` ${done} Continuous sync ${chalk14.dim("pre-commit hook keeps all agent configs in sync")}`
|
|
10472
|
+
);
|
|
10276
10473
|
console.log(` ${done} Config generated ${chalk14.dim(`score: ${afterScore.score}/100`)}`);
|
|
10277
|
-
console.log(
|
|
10474
|
+
console.log(
|
|
10475
|
+
` ${done} Agent skills ${chalk14.dim("/setup-caliber for new team members")}`
|
|
10476
|
+
);
|
|
10278
10477
|
if (hasLearnableAgent) {
|
|
10279
10478
|
console.log(` ${done} Session learning ${chalk14.dim("learns from your corrections")}`);
|
|
10280
10479
|
}
|
|
10281
10480
|
if (communitySkillsInstalled > 0) {
|
|
10282
|
-
console.log(
|
|
10481
|
+
console.log(
|
|
10482
|
+
` ${done} Community skills ${chalk14.dim(`${communitySkillsInstalled} installed for your stack`)}`
|
|
10483
|
+
);
|
|
10283
10484
|
}
|
|
10284
10485
|
console.log(chalk14.bold("\n What happens next:\n"));
|
|
10285
10486
|
console.log(chalk14.dim(" Every commit syncs your agent configs automatically."));
|
|
@@ -10296,8 +10497,10 @@ ${agentRefs.join(" ")}
|
|
|
10296
10497
|
report.markStep("Finished");
|
|
10297
10498
|
const reportPath = path28.join(process.cwd(), ".caliber", "debug-report.md");
|
|
10298
10499
|
report.write(reportPath);
|
|
10299
|
-
console.log(
|
|
10300
|
-
`))
|
|
10500
|
+
console.log(
|
|
10501
|
+
chalk14.dim(` Debug report written to ${path28.relative(process.cwd(), reportPath)}
|
|
10502
|
+
`)
|
|
10503
|
+
);
|
|
10301
10504
|
}
|
|
10302
10505
|
}
|
|
10303
10506
|
|
|
@@ -10633,18 +10836,27 @@ async function scoreCommand(options) {
|
|
|
10633
10836
|
const separator = chalk18.gray(" " + "\u2500".repeat(53));
|
|
10634
10837
|
console.log(separator);
|
|
10635
10838
|
const bin = resolveCaliber();
|
|
10636
|
-
|
|
10839
|
+
const failing = result.checks.filter((c) => !c.passed && c.maxPoints > 0).sort((a, b) => b.maxPoints - b.earnedPoints - (a.maxPoints - a.earnedPoints));
|
|
10840
|
+
if (result.score < 70 && failing.length > 0) {
|
|
10841
|
+
const topFix = failing[0];
|
|
10842
|
+
const pts = topFix.maxPoints - topFix.earnedPoints;
|
|
10637
10843
|
console.log(
|
|
10638
|
-
chalk18.gray("
|
|
10844
|
+
chalk18.gray(" Biggest gain: ") + chalk18.yellow(`+${pts} pts`) + chalk18.gray(` from "${topFix.name}"`) + (topFix.suggestion ? chalk18.gray(` \u2014 ${topFix.suggestion}`) : "")
|
|
10639
10845
|
);
|
|
10640
|
-
} else if (result.score < 70) {
|
|
10641
10846
|
console.log(
|
|
10642
|
-
chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(`${bin} init`) + chalk18.gray(" to
|
|
10847
|
+
chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(`${bin} init`) + chalk18.gray(" to auto-fix these.")
|
|
10643
10848
|
);
|
|
10644
|
-
} else {
|
|
10849
|
+
} else if (failing.length > 0) {
|
|
10645
10850
|
console.log(
|
|
10646
|
-
chalk18.green(" Looking good!") + chalk18.gray(
|
|
10851
|
+
chalk18.green(" Looking good!") + chalk18.gray(
|
|
10852
|
+
` ${failing.length} check${failing.length === 1 ? "" : "s"} can still be improved.`
|
|
10853
|
+
)
|
|
10647
10854
|
);
|
|
10855
|
+
console.log(
|
|
10856
|
+
chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(`${bin} init`) + chalk18.gray(" to improve, or ") + chalk18.hex("#83D1EB")(`${bin} regenerate`) + chalk18.gray(" to rebuild from scratch.")
|
|
10857
|
+
);
|
|
10858
|
+
} else {
|
|
10859
|
+
console.log(chalk18.green(" Perfect score! Your agent configs are fully optimized."));
|
|
10648
10860
|
}
|
|
10649
10861
|
console.log("");
|
|
10650
10862
|
}
|
|
@@ -10660,12 +10872,23 @@ import { execSync as execSync15 } from "child_process";
|
|
|
10660
10872
|
var MAX_DIFF_BYTES = 1e5;
|
|
10661
10873
|
var DOC_PATTERNS = [
|
|
10662
10874
|
"CLAUDE.md",
|
|
10875
|
+
"AGENTS.md",
|
|
10663
10876
|
"README.md",
|
|
10664
10877
|
".cursorrules",
|
|
10665
10878
|
".cursor/rules/",
|
|
10879
|
+
".cursor/skills/",
|
|
10666
10880
|
".claude/skills/",
|
|
10881
|
+
".agents/skills/",
|
|
10882
|
+
".opencode/skills/",
|
|
10883
|
+
".github/copilot-instructions.md",
|
|
10884
|
+
".github/instructions/",
|
|
10667
10885
|
"CALIBER_LEARNINGS.md"
|
|
10668
10886
|
];
|
|
10887
|
+
function truncateAtLineEnd(text, maxBytes) {
|
|
10888
|
+
if (text.length <= maxBytes) return text;
|
|
10889
|
+
const lastNewline = text.lastIndexOf("\n", maxBytes);
|
|
10890
|
+
return lastNewline === -1 ? text.slice(0, maxBytes) : text.slice(0, lastNewline);
|
|
10891
|
+
}
|
|
10669
10892
|
function excludeArgs() {
|
|
10670
10893
|
return DOC_PATTERNS.flatMap((p) => ["--", `:!${p}`]);
|
|
10671
10894
|
}
|
|
@@ -10714,9 +10937,9 @@ function collectDiff(lastSha) {
|
|
|
10714
10937
|
const totalSize = committedDiff.length + stagedDiff.length + unstagedDiff.length;
|
|
10715
10938
|
if (totalSize > MAX_DIFF_BYTES) {
|
|
10716
10939
|
const ratio = MAX_DIFF_BYTES / totalSize;
|
|
10717
|
-
committedDiff = committedDiff
|
|
10718
|
-
stagedDiff = stagedDiff
|
|
10719
|
-
unstagedDiff = unstagedDiff
|
|
10940
|
+
committedDiff = truncateAtLineEnd(committedDiff, Math.floor(committedDiff.length * ratio));
|
|
10941
|
+
stagedDiff = truncateAtLineEnd(stagedDiff, Math.floor(stagedDiff.length * ratio));
|
|
10942
|
+
unstagedDiff = truncateAtLineEnd(unstagedDiff, Math.floor(unstagedDiff.length * ratio));
|
|
10720
10943
|
}
|
|
10721
10944
|
const hasChanges = !!(committedDiff || stagedDiff || unstagedDiff || changedFiles.length);
|
|
10722
10945
|
const parts = [];
|
|
@@ -10735,11 +10958,11 @@ import path30 from "path";
|
|
|
10735
10958
|
function writeRefreshDocs(docs) {
|
|
10736
10959
|
const written = [];
|
|
10737
10960
|
if (docs.agentsMd) {
|
|
10738
|
-
fs37.writeFileSync("AGENTS.md",
|
|
10961
|
+
fs37.writeFileSync("AGENTS.md", appendManagedBlocks(docs.agentsMd, "codex"));
|
|
10739
10962
|
written.push("AGENTS.md");
|
|
10740
10963
|
}
|
|
10741
10964
|
if (docs.claudeMd) {
|
|
10742
|
-
fs37.writeFileSync("CLAUDE.md",
|
|
10965
|
+
fs37.writeFileSync("CLAUDE.md", appendManagedBlocks(docs.claudeMd));
|
|
10743
10966
|
written.push("CLAUDE.md");
|
|
10744
10967
|
}
|
|
10745
10968
|
if (docs.readmeMd) {
|
|
@@ -10758,17 +10981,12 @@ function writeRefreshDocs(docs) {
|
|
|
10758
10981
|
written.push(`.cursor/rules/${rule.filename}`);
|
|
10759
10982
|
}
|
|
10760
10983
|
}
|
|
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
10984
|
if (docs.copilotInstructions) {
|
|
10770
10985
|
fs37.mkdirSync(".github", { recursive: true });
|
|
10771
|
-
fs37.writeFileSync(
|
|
10986
|
+
fs37.writeFileSync(
|
|
10987
|
+
path30.join(".github", "copilot-instructions.md"),
|
|
10988
|
+
appendManagedBlocks(docs.copilotInstructions, "copilot")
|
|
10989
|
+
);
|
|
10772
10990
|
written.push(".github/copilot-instructions.md");
|
|
10773
10991
|
}
|
|
10774
10992
|
if (docs.copilotInstructionFiles) {
|
|
@@ -10784,6 +11002,7 @@ function writeRefreshDocs(docs) {
|
|
|
10784
11002
|
|
|
10785
11003
|
// src/ai/refresh.ts
|
|
10786
11004
|
init_config();
|
|
11005
|
+
init_pre_commit_block();
|
|
10787
11006
|
async function refreshDocs(diff, existingDocs, projectContext, learnedSection, sources2) {
|
|
10788
11007
|
const prompt = buildRefreshPrompt(diff, existingDocs, projectContext, learnedSection, sources2);
|
|
10789
11008
|
const fastModel = getFastModel();
|
|
@@ -10799,13 +11018,17 @@ function buildRefreshPrompt(diff, existingDocs, projectContext, learnedSection,
|
|
|
10799
11018
|
const parts = [];
|
|
10800
11019
|
parts.push("Update documentation based on the following code changes.\n");
|
|
10801
11020
|
if (projectContext.packageName) parts.push(`Project: ${projectContext.packageName}`);
|
|
10802
|
-
if (projectContext.languages?.length)
|
|
10803
|
-
|
|
11021
|
+
if (projectContext.languages?.length)
|
|
11022
|
+
parts.push(`Languages: ${projectContext.languages.join(", ")}`);
|
|
11023
|
+
if (projectContext.frameworks?.length)
|
|
11024
|
+
parts.push(`Frameworks: ${projectContext.frameworks.join(", ")}`);
|
|
10804
11025
|
if (projectContext.fileTree?.length) {
|
|
10805
11026
|
const tree = projectContext.fileTree.slice(0, 200);
|
|
10806
|
-
parts.push(
|
|
11027
|
+
parts.push(
|
|
11028
|
+
`
|
|
10807
11029
|
File tree (${tree.length}/${projectContext.fileTree.length} \u2014 only reference paths from this list):
|
|
10808
|
-
${tree.join("\n")}`
|
|
11030
|
+
${tree.join("\n")}`
|
|
11031
|
+
);
|
|
10809
11032
|
}
|
|
10810
11033
|
parts.push(`
|
|
10811
11034
|
Changed files: ${diff.changedFiles.join(", ")}`);
|
|
@@ -10825,11 +11048,11 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
10825
11048
|
parts.push("\n--- Current Documentation ---");
|
|
10826
11049
|
if (existingDocs.agentsMd) {
|
|
10827
11050
|
parts.push("\n[AGENTS.md]");
|
|
10828
|
-
parts.push(existingDocs.agentsMd);
|
|
11051
|
+
parts.push(stripManagedBlocks(existingDocs.agentsMd));
|
|
10829
11052
|
}
|
|
10830
11053
|
if (existingDocs.claudeMd) {
|
|
10831
11054
|
parts.push("\n[CLAUDE.md]");
|
|
10832
|
-
parts.push(existingDocs.claudeMd);
|
|
11055
|
+
parts.push(stripManagedBlocks(existingDocs.claudeMd));
|
|
10833
11056
|
}
|
|
10834
11057
|
if (existingDocs.readmeMd) {
|
|
10835
11058
|
parts.push("\n[README.md]");
|
|
@@ -10839,20 +11062,25 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
10839
11062
|
parts.push("\n[.cursorrules]");
|
|
10840
11063
|
parts.push(existingDocs.cursorrules);
|
|
10841
11064
|
}
|
|
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
11065
|
if (existingDocs.cursorRules?.length) {
|
|
10850
11066
|
for (const rule of existingDocs.cursorRules) {
|
|
11067
|
+
if (rule.filename.startsWith("caliber-")) continue;
|
|
10851
11068
|
parts.push(`
|
|
10852
11069
|
[.cursor/rules/${rule.filename}]`);
|
|
10853
11070
|
parts.push(rule.content);
|
|
10854
11071
|
}
|
|
10855
11072
|
}
|
|
11073
|
+
if (existingDocs.copilotInstructions) {
|
|
11074
|
+
parts.push("\n[.github/copilot-instructions.md]");
|
|
11075
|
+
parts.push(stripManagedBlocks(existingDocs.copilotInstructions));
|
|
11076
|
+
}
|
|
11077
|
+
if (existingDocs.copilotInstructionFiles?.length) {
|
|
11078
|
+
for (const file of existingDocs.copilotInstructionFiles) {
|
|
11079
|
+
parts.push(`
|
|
11080
|
+
[.github/instructions/${file.filename}]`);
|
|
11081
|
+
parts.push(file.content);
|
|
11082
|
+
}
|
|
11083
|
+
}
|
|
10856
11084
|
if (learnedSection) {
|
|
10857
11085
|
parts.push("\n--- Learned Patterns (from session learning) ---");
|
|
10858
11086
|
parts.push("Consider these accumulated learnings when deciding what to update:");
|
|
@@ -11062,6 +11290,40 @@ function migrateInlineLearnings() {
|
|
|
11062
11290
|
init_config();
|
|
11063
11291
|
init_resolve_caliber();
|
|
11064
11292
|
init_builtin_skills();
|
|
11293
|
+
function writeRefreshError(error) {
|
|
11294
|
+
try {
|
|
11295
|
+
if (!fs40.existsSync(CALIBER_DIR)) fs40.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
11296
|
+
fs40.writeFileSync(
|
|
11297
|
+
REFRESH_LAST_ERROR_FILE,
|
|
11298
|
+
JSON.stringify(
|
|
11299
|
+
{
|
|
11300
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11301
|
+
error: error instanceof Error ? error.message : String(error),
|
|
11302
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
11303
|
+
cwd: process.cwd(),
|
|
11304
|
+
nodeVersion: process.version
|
|
11305
|
+
},
|
|
11306
|
+
null,
|
|
11307
|
+
2
|
|
11308
|
+
)
|
|
11309
|
+
);
|
|
11310
|
+
} catch {
|
|
11311
|
+
}
|
|
11312
|
+
}
|
|
11313
|
+
function readRefreshError() {
|
|
11314
|
+
try {
|
|
11315
|
+
if (!fs40.existsSync(REFRESH_LAST_ERROR_FILE)) return null;
|
|
11316
|
+
return JSON.parse(fs40.readFileSync(REFRESH_LAST_ERROR_FILE, "utf-8"));
|
|
11317
|
+
} catch {
|
|
11318
|
+
return null;
|
|
11319
|
+
}
|
|
11320
|
+
}
|
|
11321
|
+
function clearRefreshError() {
|
|
11322
|
+
try {
|
|
11323
|
+
if (fs40.existsSync(REFRESH_LAST_ERROR_FILE)) fs40.unlinkSync(REFRESH_LAST_ERROR_FILE);
|
|
11324
|
+
} catch {
|
|
11325
|
+
}
|
|
11326
|
+
}
|
|
11065
11327
|
function log2(quiet, ...args) {
|
|
11066
11328
|
if (!quiet) console.log(...args);
|
|
11067
11329
|
}
|
|
@@ -11080,21 +11342,41 @@ function discoverGitRepos(parentDir) {
|
|
|
11080
11342
|
}
|
|
11081
11343
|
return repos.sort();
|
|
11082
11344
|
}
|
|
11345
|
+
function collectFilesToWrite(updatedDocs) {
|
|
11346
|
+
const files = [];
|
|
11347
|
+
if (updatedDocs.agentsMd) files.push("AGENTS.md");
|
|
11348
|
+
if (updatedDocs.claudeMd) files.push("CLAUDE.md");
|
|
11349
|
+
if (updatedDocs.readmeMd) files.push("README.md");
|
|
11350
|
+
if (updatedDocs.cursorrules) files.push(".cursorrules");
|
|
11351
|
+
if (Array.isArray(updatedDocs.cursorRules)) {
|
|
11352
|
+
for (const r of updatedDocs.cursorRules)
|
|
11353
|
+
files.push(`.cursor/rules/${r.filename}`);
|
|
11354
|
+
}
|
|
11355
|
+
if (updatedDocs.copilotInstructions) files.push(".github/copilot-instructions.md");
|
|
11356
|
+
if (Array.isArray(updatedDocs.copilotInstructionFiles)) {
|
|
11357
|
+
for (const f of updatedDocs.copilotInstructionFiles)
|
|
11358
|
+
files.push(`.github/instructions/${f.filename}`);
|
|
11359
|
+
}
|
|
11360
|
+
return files;
|
|
11361
|
+
}
|
|
11083
11362
|
var REFRESH_COOLDOWN_MS = 3e4;
|
|
11084
11363
|
async function refreshSingleRepo(repoDir, options) {
|
|
11085
11364
|
const quiet = !!options.quiet;
|
|
11086
11365
|
const prefix = options.label ? `${chalk19.bold(options.label)} ` : "";
|
|
11087
11366
|
const state = readState();
|
|
11088
11367
|
const lastSha = state?.lastRefreshSha ?? null;
|
|
11089
|
-
|
|
11368
|
+
const currentSha = getCurrentHeadSha();
|
|
11369
|
+
if (state?.lastRefreshTimestamp && lastSha && currentSha === lastSha) {
|
|
11090
11370
|
const elapsed = Date.now() - new Date(state.lastRefreshTimestamp).getTime();
|
|
11091
11371
|
if (elapsed < REFRESH_COOLDOWN_MS && elapsed > 0) {
|
|
11092
|
-
log2(
|
|
11372
|
+
log2(
|
|
11373
|
+
quiet,
|
|
11374
|
+
chalk19.dim(`${prefix}Skipped \u2014 last refresh was ${Math.round(elapsed / 1e3)}s ago.`)
|
|
11375
|
+
);
|
|
11093
11376
|
return;
|
|
11094
11377
|
}
|
|
11095
11378
|
}
|
|
11096
11379
|
const diff = collectDiff(lastSha);
|
|
11097
|
-
const currentSha = getCurrentHeadSha();
|
|
11098
11380
|
if (!diff.hasChanges) {
|
|
11099
11381
|
if (currentSha) {
|
|
11100
11382
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -11103,9 +11385,9 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11103
11385
|
return;
|
|
11104
11386
|
}
|
|
11105
11387
|
const spinner = quiet ? null : ora6(`${prefix}Analyzing changes...`).start();
|
|
11106
|
-
const existingDocs = readExistingConfigs(repoDir);
|
|
11107
11388
|
const learnedSection = readLearnedSection();
|
|
11108
11389
|
const fingerprint = await collectFingerprint(repoDir);
|
|
11390
|
+
const existingDocs = fingerprint.existingConfigs;
|
|
11109
11391
|
const projectContext = {
|
|
11110
11392
|
languages: fingerprint.languages,
|
|
11111
11393
|
frameworks: fingerprint.frameworks,
|
|
@@ -11124,12 +11406,24 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11124
11406
|
const sourcesPayload = sources2.length > 0 ? sources2 : void 0;
|
|
11125
11407
|
let response;
|
|
11126
11408
|
try {
|
|
11127
|
-
response = await refreshDocs(
|
|
11409
|
+
response = await refreshDocs(
|
|
11410
|
+
diffPayload,
|
|
11411
|
+
existingDocs,
|
|
11412
|
+
projectContext,
|
|
11413
|
+
learnedSection,
|
|
11414
|
+
sourcesPayload
|
|
11415
|
+
);
|
|
11128
11416
|
} catch (firstErr) {
|
|
11129
11417
|
const isTransient = firstErr instanceof Error && TRANSIENT_ERRORS.some((e) => firstErr.message.toLowerCase().includes(e.toLowerCase()));
|
|
11130
11418
|
if (!isTransient) throw firstErr;
|
|
11131
11419
|
try {
|
|
11132
|
-
response = await refreshDocs(
|
|
11420
|
+
response = await refreshDocs(
|
|
11421
|
+
diffPayload,
|
|
11422
|
+
existingDocs,
|
|
11423
|
+
projectContext,
|
|
11424
|
+
learnedSection,
|
|
11425
|
+
sourcesPayload
|
|
11426
|
+
);
|
|
11133
11427
|
} catch {
|
|
11134
11428
|
spinner?.fail(`${prefix}Refresh failed after retry`);
|
|
11135
11429
|
throw firstErr;
|
|
@@ -11155,9 +11449,9 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11155
11449
|
}
|
|
11156
11450
|
const targetAgent = state?.targetAgent ?? detectTargetAgent(repoDir);
|
|
11157
11451
|
const preScore = computeLocalScore(repoDir, targetAgent);
|
|
11158
|
-
const
|
|
11452
|
+
const allFilesToWrite = collectFilesToWrite(response.updatedDocs);
|
|
11159
11453
|
const preRefreshContents = /* @__PURE__ */ new Map();
|
|
11160
|
-
for (const filePath of
|
|
11454
|
+
for (const filePath of allFilesToWrite) {
|
|
11161
11455
|
const fullPath = path33.resolve(repoDir, filePath);
|
|
11162
11456
|
try {
|
|
11163
11457
|
preRefreshContents.set(filePath, fs40.readFileSync(fullPath, "utf-8"));
|
|
@@ -11181,7 +11475,9 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11181
11475
|
fs40.writeFileSync(fullPath, content);
|
|
11182
11476
|
}
|
|
11183
11477
|
}
|
|
11184
|
-
spinner?.warn(
|
|
11478
|
+
spinner?.warn(
|
|
11479
|
+
`${prefix}Refresh reverted \u2014 score would drop from ${preScore.score} to ${postScore.score}`
|
|
11480
|
+
);
|
|
11185
11481
|
log2(quiet, chalk19.dim(` Config quality gate prevented a regression. No files were changed.`));
|
|
11186
11482
|
if (currentSha) {
|
|
11187
11483
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -11201,6 +11497,7 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11201
11497
|
for (const file of builtinWritten) {
|
|
11202
11498
|
log2(quiet, ` ${chalk19.green("\u2713")} ${file} ${chalk19.dim("(built-in)")}`);
|
|
11203
11499
|
}
|
|
11500
|
+
clearRefreshError();
|
|
11204
11501
|
if (currentSha) {
|
|
11205
11502
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
11206
11503
|
}
|
|
@@ -11211,11 +11508,28 @@ async function refreshCommand(options) {
|
|
|
11211
11508
|
const { isCaliberRunning: isCaliberRunning2 } = await Promise.resolve().then(() => (init_lock(), lock_exports));
|
|
11212
11509
|
if (isCaliberRunning2()) return;
|
|
11213
11510
|
}
|
|
11511
|
+
if (!quiet) {
|
|
11512
|
+
const lastError = readRefreshError();
|
|
11513
|
+
if (lastError) {
|
|
11514
|
+
console.log(chalk19.yellow(`
|
|
11515
|
+
\u26A0 Last refresh failed (${lastError.timestamp}):`));
|
|
11516
|
+
console.log(chalk19.dim(` ${lastError.error}`));
|
|
11517
|
+
console.log(
|
|
11518
|
+
chalk19.dim(
|
|
11519
|
+
` Run with --debug for full details, or report at https://github.com/caliber-ai-org/ai-setup/issues
|
|
11520
|
+
`
|
|
11521
|
+
)
|
|
11522
|
+
);
|
|
11523
|
+
clearRefreshError();
|
|
11524
|
+
}
|
|
11525
|
+
}
|
|
11214
11526
|
try {
|
|
11215
11527
|
const config = loadConfig();
|
|
11216
11528
|
if (!config) {
|
|
11217
11529
|
if (quiet) return;
|
|
11218
|
-
console.log(
|
|
11530
|
+
console.log(
|
|
11531
|
+
chalk19.red("No LLM provider configured. Run ") + chalk19.hex("#83D1EB")(`${resolveCaliber()} config`) + chalk19.red(" (e.g. choose Cursor) or set an API key.")
|
|
11532
|
+
);
|
|
11219
11533
|
throw new Error("__exit__");
|
|
11220
11534
|
}
|
|
11221
11535
|
await validateModel({ fast: true });
|
|
@@ -11226,7 +11540,9 @@ async function refreshCommand(options) {
|
|
|
11226
11540
|
const repos = discoverGitRepos(process.cwd());
|
|
11227
11541
|
if (repos.length === 0) {
|
|
11228
11542
|
if (quiet) return;
|
|
11229
|
-
console.log(
|
|
11543
|
+
console.log(
|
|
11544
|
+
chalk19.red("Not inside a git repository and no git repos found in child directories.")
|
|
11545
|
+
);
|
|
11230
11546
|
throw new Error("__exit__");
|
|
11231
11547
|
}
|
|
11232
11548
|
log2(quiet, chalk19.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
|
|
@@ -11239,12 +11555,19 @@ async function refreshCommand(options) {
|
|
|
11239
11555
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
11240
11556
|
} catch (err) {
|
|
11241
11557
|
if (err instanceof Error && err.message === "__exit__") continue;
|
|
11242
|
-
|
|
11558
|
+
writeRefreshError(err);
|
|
11559
|
+
log2(
|
|
11560
|
+
quiet,
|
|
11561
|
+
chalk19.yellow(
|
|
11562
|
+
`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`
|
|
11563
|
+
)
|
|
11564
|
+
);
|
|
11243
11565
|
}
|
|
11244
11566
|
}
|
|
11245
11567
|
process.chdir(originalDir);
|
|
11246
11568
|
} catch (err) {
|
|
11247
11569
|
if (err instanceof Error && err.message === "__exit__") throw err;
|
|
11570
|
+
writeRefreshError(err);
|
|
11248
11571
|
if (quiet) return;
|
|
11249
11572
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
11250
11573
|
console.log(chalk19.red(`Refresh failed: ${msg}`));
|
|
@@ -11589,10 +11912,10 @@ function writeState2(state) {
|
|
|
11589
11912
|
function resetState() {
|
|
11590
11913
|
writeState2({ ...DEFAULT_STATE });
|
|
11591
11914
|
}
|
|
11592
|
-
var
|
|
11915
|
+
var LOCK_FILE = "finalize.lock";
|
|
11593
11916
|
var LOCK_STALE_MS = 5 * 60 * 1e3;
|
|
11594
11917
|
function lockFilePath() {
|
|
11595
|
-
return path34.join(getLearningDir(),
|
|
11918
|
+
return path34.join(getLearningDir(), LOCK_FILE);
|
|
11596
11919
|
}
|
|
11597
11920
|
function acquireFinalizeLock() {
|
|
11598
11921
|
ensureLearningDir();
|
package/package.json
CHANGED