@rely-ai/caliber 1.35.1 → 1.37.0-dev.1774891449
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 +82 -56
- package/dist/bin.js +490 -143
- 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 {
|
|
@@ -3056,6 +3101,7 @@ var SKILL_FORMAT_RULES = `All skills follow the OpenSkills standard (agentskills
|
|
|
3056
3101
|
|
|
3057
3102
|
Skill field requirements:
|
|
3058
3103
|
- "name": kebab-case (lowercase letters, numbers, hyphens only). Becomes the directory name.
|
|
3104
|
+
- "name" MUST NOT be any of these reserved names (they are managed by Caliber automatically): "setup-caliber", "find-skills", "save-learning". Do NOT generate skills with these names.
|
|
3059
3105
|
- "description": MUST include WHAT it does + WHEN to use it with specific trigger phrases. Example: "Manages database migrations. Use when user says 'run migration', 'create migration', 'db schema change', or modifies files in db/migrations/."
|
|
3060
3106
|
- "content": markdown body only \u2014 do NOT include YAML frontmatter, it is generated from name+description.
|
|
3061
3107
|
|
|
@@ -3099,7 +3145,7 @@ PRIORITY WHEN CONSTRAINTS CONFLICT: Grounding and reference density matter more
|
|
|
3099
3145
|
Note: Permissions, hooks, freshness tracking, and OpenSkills frontmatter are scored automatically by caliber \u2014 do not optimize for them.
|
|
3100
3146
|
README.md is provided for context only \u2014 do NOT include a readmeMd field in your output.`;
|
|
3101
3147
|
var OUTPUT_SIZE_CONSTRAINTS = `OUTPUT SIZE CONSTRAINTS \u2014 these are critical:
|
|
3102
|
-
- CLAUDE.md / AGENTS.md: MUST be under
|
|
3148
|
+
- CLAUDE.md / AGENTS.md: MUST be under 400 lines for maximum score. Aim for 200-350 lines. Be thorough \u2014 commands, architecture overview, key conventions, data flow, and important patterns. Use bullet points and tables, not prose.
|
|
3103
3149
|
|
|
3104
3150
|
Pack project references densely in architecture sections \u2014 use inline paths, not prose paragraphs:
|
|
3105
3151
|
GOOD: **Entry**: \`src/bin.ts\` \u2192 \`src/cli.ts\` \xB7 **LLM** (\`src/llm/\`): \`anthropic.ts\` \xB7 \`vertex.ts\` \xB7 \`openai-compat.ts\`
|
|
@@ -3237,7 +3283,7 @@ Structure:
|
|
|
3237
3283
|
5. "## Common Issues" (required) \u2014 specific error messages and their fixes. Not "check your config" but "If you see 'Connection refused on port 5432': 1. Verify postgres is running: docker ps | grep postgres 2. Check .env has correct DATABASE_URL"
|
|
3238
3284
|
|
|
3239
3285
|
Rules:
|
|
3240
|
-
- Max
|
|
3286
|
+
- Max 400 lines. Focus on actionable instructions, not documentation prose.
|
|
3241
3287
|
- Study existing code in the project context to extract the real patterns being used. A skill for "create API route" should show the exact file structure, imports, error handling, and naming that existing routes use.
|
|
3242
3288
|
- Be specific and actionable. GOOD: "Run \`pnpm test -- --filter=api\` to verify". BAD: "Validate the data before proceeding."
|
|
3243
3289
|
- Never use ambiguous language. Instead of "handle errors properly", write "Wrap the DB call in try/catch. On failure, return { error: string, code: number } matching the ErrorResponse type in \`src/types.ts\`."
|
|
@@ -3295,7 +3341,7 @@ Rules:
|
|
|
3295
3341
|
- Update the "fileDescriptions" to reflect any changes you make.
|
|
3296
3342
|
|
|
3297
3343
|
Quality constraints \u2014 your changes are scored, so do not break these:
|
|
3298
|
-
- CLAUDE.md / AGENTS.md: MUST stay under
|
|
3344
|
+
- CLAUDE.md / AGENTS.md: MUST stay under 400 lines. If adding content, remove less important lines to stay within budget. Do not refuse the user's request \u2014 make the change and trim elsewhere.
|
|
3299
3345
|
- Avoid vague instructions ("follow best practices", "write clean code", "ensure quality").
|
|
3300
3346
|
- Do NOT add directory tree listings in code blocks.
|
|
3301
3347
|
- Do NOT remove existing code blocks \u2014 they contribute to the executable content score.
|
|
@@ -3319,14 +3365,20 @@ CONSERVATIVE UPDATE means:
|
|
|
3319
3365
|
- NEVER replace specific paths/commands with generic prose
|
|
3320
3366
|
|
|
3321
3367
|
Quality constraints (the output is scored deterministically):
|
|
3322
|
-
- CLAUDE.md / AGENTS.md: MUST stay under
|
|
3368
|
+
- CLAUDE.md / AGENTS.md: MUST stay under 400 lines. If the diff adds content, trim the least important lines elsewhere.
|
|
3323
3369
|
- Keep 3+ code blocks with executable commands \u2014 do not remove code blocks
|
|
3324
3370
|
- Every file path, command, and identifier must be in backticks
|
|
3325
3371
|
- ONLY reference file paths that exist in the provided file tree \u2014 do NOT invent paths
|
|
3326
3372
|
- Preserve the existing structure (headings, bullet style, formatting)
|
|
3327
3373
|
|
|
3374
|
+
Cross-agent sync:
|
|
3375
|
+
- When a code change affects documentation, update ALL provided platform configs together.
|
|
3376
|
+
- A renamed command, moved file, or changed convention must be reflected in every config (CLAUDE.md, AGENTS.md, copilot instructions, skills across all platforms).
|
|
3377
|
+
- Cross-agent consistency is critical \u2014 all agents working on this repo must have the same, accurate context.
|
|
3378
|
+
|
|
3328
3379
|
Managed content:
|
|
3329
3380
|
- Keep managed blocks (<!-- caliber:managed --> ... <!-- /caliber:managed -->) intact
|
|
3381
|
+
- Keep context sync blocks (<!-- caliber:managed:sync --> ... <!-- /caliber:managed:sync -->) intact
|
|
3330
3382
|
- Do NOT modify CALIBER_LEARNINGS.md \u2014 it is managed separately
|
|
3331
3383
|
- Preserve any references to CALIBER_LEARNINGS.md in CLAUDE.md
|
|
3332
3384
|
|
|
@@ -3338,14 +3390,16 @@ Return a JSON object with this exact shape:
|
|
|
3338
3390
|
"readmeMd": "<updated content or null>",
|
|
3339
3391
|
"cursorrules": "<updated content or null>",
|
|
3340
3392
|
"cursorRules": [{"filename": "name.mdc", "content": "..."}] or null,
|
|
3341
|
-
"claudeSkills": [{"filename": "name.md", "content": "..."}] or null,
|
|
3342
3393
|
"copilotInstructions": "<updated content or null>",
|
|
3343
3394
|
"copilotInstructionFiles": [{"filename": "name.instructions.md", "content": "..."}] or null
|
|
3344
3395
|
},
|
|
3345
3396
|
"changesSummary": "<1-2 sentence summary of what was updated and why>",
|
|
3397
|
+
"fileChanges": [{"file": "CLAUDE.md", "description": "added new API routes, updated build commands"}],
|
|
3346
3398
|
"docsUpdated": ["CLAUDE.md", "README.md"]
|
|
3347
3399
|
}
|
|
3348
3400
|
|
|
3401
|
+
The "fileChanges" array MUST include one entry per file that was updated (non-null in updatedDocs). Each entry describes what specifically changed in that file \u2014 be concrete (e.g. "added auth middleware section" not "updated docs").
|
|
3402
|
+
|
|
3349
3403
|
Respond with ONLY the JSON object, no markdown fences or extra text.`;
|
|
3350
3404
|
var LEARN_SYSTEM_PROMPT = `You are an expert developer experience engineer. You analyze raw tool call events from AI coding sessions to extract reusable operational lessons that will help future LLM sessions work more effectively in this project.
|
|
3351
3405
|
|
|
@@ -3930,7 +3984,7 @@ var STOP_HOOK_SCRIPT_CONTENT = `#!/bin/sh
|
|
|
3930
3984
|
if grep -q "caliber" .git/hooks/pre-commit 2>/dev/null; then
|
|
3931
3985
|
exit 0
|
|
3932
3986
|
fi
|
|
3933
|
-
FLAG="/tmp/caliber-nudge-$(echo "$PWD" | shasum | cut -c1-8)"
|
|
3987
|
+
FLAG="/tmp/caliber-nudge-$(echo "$PWD" | (shasum 2>/dev/null || sha1sum 2>/dev/null || md5sum 2>/dev/null || cksum) | cut -c1-8)"
|
|
3934
3988
|
find /tmp -maxdepth 1 -name "caliber-nudge-*" -mmin +120 -delete 2>/dev/null
|
|
3935
3989
|
if [ -f "$FLAG" ]; then
|
|
3936
3990
|
exit 0
|
|
@@ -3961,11 +4015,13 @@ function installStopHook() {
|
|
|
3961
4015
|
}
|
|
3962
4016
|
settings.hooks.Stop.push({
|
|
3963
4017
|
matcher: "",
|
|
3964
|
-
hooks: [
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
4018
|
+
hooks: [
|
|
4019
|
+
{
|
|
4020
|
+
type: "command",
|
|
4021
|
+
command: STOP_HOOK_SCRIPT_PATH,
|
|
4022
|
+
description: STOP_HOOK_DESCRIPTION
|
|
4023
|
+
}
|
|
4024
|
+
]
|
|
3969
4025
|
});
|
|
3970
4026
|
writeSettings(settings);
|
|
3971
4027
|
return { installed: true, alreadyInstalled: false };
|
|
@@ -3997,16 +4053,20 @@ function getPrecommitBlock() {
|
|
|
3997
4053
|
const invoke = npx ? bin : `"${bin}"`;
|
|
3998
4054
|
return `${PRECOMMIT_START}
|
|
3999
4055
|
if ${guard}; then
|
|
4056
|
+
mkdir -p .caliber
|
|
4000
4057
|
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
|
|
4058
|
+
${invoke} refresh --quiet 2>.caliber/refresh-hook.log || true
|
|
4059
|
+
${invoke} learn finalize 2>>.caliber/refresh-hook.log || true
|
|
4060
|
+
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
4061
|
fi
|
|
4005
4062
|
${PRECOMMIT_END}`;
|
|
4006
4063
|
}
|
|
4007
4064
|
function getGitHooksDir() {
|
|
4008
4065
|
try {
|
|
4009
|
-
const gitDir = execSync8("git rev-parse --git-dir", {
|
|
4066
|
+
const gitDir = execSync8("git rev-parse --git-dir", {
|
|
4067
|
+
encoding: "utf-8",
|
|
4068
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4069
|
+
}).trim();
|
|
4010
4070
|
return path10.join(gitDir, "hooks");
|
|
4011
4071
|
} catch {
|
|
4012
4072
|
return null;
|
|
@@ -5199,7 +5259,7 @@ import fs12 from "fs";
|
|
|
5199
5259
|
import path12 from "path";
|
|
5200
5260
|
function writeClaudeConfig(config) {
|
|
5201
5261
|
const written = [];
|
|
5202
|
-
fs12.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(config.claudeMd)));
|
|
5262
|
+
fs12.writeFileSync("CLAUDE.md", appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(config.claudeMd))));
|
|
5203
5263
|
written.push("CLAUDE.md");
|
|
5204
5264
|
if (config.skills?.length) {
|
|
5205
5265
|
for (const skill of config.skills) {
|
|
@@ -5245,7 +5305,8 @@ function writeCursorConfig(config) {
|
|
|
5245
5305
|
}
|
|
5246
5306
|
const preCommitRule = getCursorPreCommitRule();
|
|
5247
5307
|
const learningsRule = getCursorLearningsRule();
|
|
5248
|
-
const
|
|
5308
|
+
const syncRule = getCursorSyncRule();
|
|
5309
|
+
const allRules = [...config.rules || [], preCommitRule, learningsRule, syncRule];
|
|
5249
5310
|
const rulesDir = path13.join(".cursor", "rules");
|
|
5250
5311
|
if (!fs13.existsSync(rulesDir)) fs13.mkdirSync(rulesDir, { recursive: true });
|
|
5251
5312
|
for (const rule of allRules) {
|
|
@@ -5294,7 +5355,10 @@ import fs14 from "fs";
|
|
|
5294
5355
|
import path14 from "path";
|
|
5295
5356
|
function writeCodexConfig(config) {
|
|
5296
5357
|
const written = [];
|
|
5297
|
-
fs14.writeFileSync(
|
|
5358
|
+
fs14.writeFileSync(
|
|
5359
|
+
"AGENTS.md",
|
|
5360
|
+
appendLearningsBlock(appendPreCommitBlock(config.agentsMd, "codex"))
|
|
5361
|
+
);
|
|
5298
5362
|
written.push("AGENTS.md");
|
|
5299
5363
|
if (config.skills?.length) {
|
|
5300
5364
|
for (const skill of config.skills) {
|
|
@@ -5323,7 +5387,10 @@ function writeGithubCopilotConfig(config) {
|
|
|
5323
5387
|
const written = [];
|
|
5324
5388
|
if (config.instructions) {
|
|
5325
5389
|
fs15.mkdirSync(".github", { recursive: true });
|
|
5326
|
-
fs15.writeFileSync(
|
|
5390
|
+
fs15.writeFileSync(
|
|
5391
|
+
path15.join(".github", "copilot-instructions.md"),
|
|
5392
|
+
appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(config.instructions, "copilot")))
|
|
5393
|
+
);
|
|
5327
5394
|
written.push(".github/copilot-instructions.md");
|
|
5328
5395
|
}
|
|
5329
5396
|
if (config.instructionFiles?.length) {
|
|
@@ -5345,7 +5412,10 @@ import path17 from "path";
|
|
|
5345
5412
|
function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
|
|
5346
5413
|
const written = [];
|
|
5347
5414
|
if (!agentsMdAlreadyWritten) {
|
|
5348
|
-
fs17.writeFileSync(
|
|
5415
|
+
fs17.writeFileSync(
|
|
5416
|
+
"AGENTS.md",
|
|
5417
|
+
appendLearningsBlock(appendPreCommitBlock(config.agentsMd, "codex"))
|
|
5418
|
+
);
|
|
5349
5419
|
written.push("AGENTS.md");
|
|
5350
5420
|
}
|
|
5351
5421
|
if (config.skills?.length) {
|
|
@@ -6052,11 +6122,11 @@ var POINTS_LEARNED_CONTENT = 2;
|
|
|
6052
6122
|
var POINTS_SOURCES_CONFIGURED = 3;
|
|
6053
6123
|
var POINTS_SOURCES_REFERENCED = 3;
|
|
6054
6124
|
var TOKEN_BUDGET_THRESHOLDS = [
|
|
6055
|
-
{ maxTokens:
|
|
6056
|
-
{ maxTokens:
|
|
6057
|
-
{ maxTokens:
|
|
6058
|
-
{ maxTokens:
|
|
6059
|
-
{ maxTokens:
|
|
6125
|
+
{ maxTokens: 5e3, points: 6 },
|
|
6126
|
+
{ maxTokens: 8e3, points: 5 },
|
|
6127
|
+
{ maxTokens: 12e3, points: 4 },
|
|
6128
|
+
{ maxTokens: 16e3, points: 2 },
|
|
6129
|
+
{ maxTokens: 24e3, points: 1 }
|
|
6060
6130
|
];
|
|
6061
6131
|
var CODE_BLOCK_THRESHOLDS = [
|
|
6062
6132
|
{ minBlocks: 3, points: 8 },
|
|
@@ -9686,27 +9756,36 @@ async function initCommand(options) {
|
|
|
9686
9756
|
const bin = resolveCaliber();
|
|
9687
9757
|
const firstRun = isFirstRun(process.cwd());
|
|
9688
9758
|
if (firstRun) {
|
|
9689
|
-
console.log(
|
|
9759
|
+
console.log(
|
|
9760
|
+
brand.bold(`
|
|
9690
9761
|
\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
9762
|
\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
9763
|
\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
9764
|
\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
9765
|
\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
9766
|
\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
|
-
`)
|
|
9767
|
+
`)
|
|
9768
|
+
);
|
|
9697
9769
|
console.log(chalk14.dim(" Keep your AI agent configs in sync \u2014 automatically."));
|
|
9698
9770
|
console.log(chalk14.dim(" Works across Claude Code, Cursor, Codex, and GitHub Copilot.\n"));
|
|
9699
9771
|
console.log(title.bold(" How it works:\n"));
|
|
9700
|
-
console.log(chalk14.dim(" 1. Connect
|
|
9701
|
-
console.log(chalk14.dim(" 2.
|
|
9702
|
-
console.log(chalk14.dim(" 3.
|
|
9772
|
+
console.log(chalk14.dim(" 1. Connect Link your LLM provider and select your agents"));
|
|
9773
|
+
console.log(chalk14.dim(" 2. Setup Detect stack, install sync hooks & skills"));
|
|
9774
|
+
console.log(chalk14.dim(" 3. Generate Audit existing config or generate from scratch"));
|
|
9775
|
+
console.log(chalk14.dim(" 4. Finalize Review changes and score your setup\n"));
|
|
9703
9776
|
} else {
|
|
9704
9777
|
console.log(brand.bold("\n CALIBER") + chalk14.dim(" \u2014 setting up continuous sync\n"));
|
|
9705
9778
|
}
|
|
9706
9779
|
const platforms = detectPlatforms();
|
|
9707
9780
|
if (!platforms.claude && !platforms.cursor && !platforms.codex && !platforms.opencode) {
|
|
9708
|
-
console.log(
|
|
9709
|
-
|
|
9781
|
+
console.log(
|
|
9782
|
+
chalk14.yellow(" \u26A0 No supported AI platforms detected (Claude, Cursor, Codex, OpenCode).")
|
|
9783
|
+
);
|
|
9784
|
+
console.log(
|
|
9785
|
+
chalk14.yellow(
|
|
9786
|
+
" Caliber will still generate config files, but they won't be auto-installed.\n"
|
|
9787
|
+
)
|
|
9788
|
+
);
|
|
9710
9789
|
}
|
|
9711
9790
|
const report = options.debugReport ? new DebugReport() : null;
|
|
9712
9791
|
console.log(title.bold(" Step 1/3 \u2014 Connect\n"));
|
|
@@ -9761,9 +9840,12 @@ async function initCommand(options) {
|
|
|
9761
9840
|
console.log(chalk14.dim(modelLine + "\n"));
|
|
9762
9841
|
if (report) {
|
|
9763
9842
|
report.markStep("Provider connection");
|
|
9764
|
-
report.addSection(
|
|
9843
|
+
report.addSection(
|
|
9844
|
+
"LLM Provider",
|
|
9845
|
+
`- **Provider**: ${config.provider}
|
|
9765
9846
|
- **Model**: ${displayModel}
|
|
9766
|
-
- **Fast model**: ${fastModel || "none"}`
|
|
9847
|
+
- **Fast model**: ${fastModel || "none"}`
|
|
9848
|
+
);
|
|
9767
9849
|
}
|
|
9768
9850
|
await validateModel({ fast: true });
|
|
9769
9851
|
let targetAgent;
|
|
@@ -9788,10 +9870,11 @@ async function initCommand(options) {
|
|
|
9788
9870
|
console.log(chalk14.dim(` Target: ${targetAgent.join(", ")}
|
|
9789
9871
|
`));
|
|
9790
9872
|
trackInitAgentSelected(targetAgent, agentAutoDetected);
|
|
9791
|
-
console.log(title.bold(" Step 2/
|
|
9873
|
+
console.log(title.bold(" Step 2/4 \u2014 Setup\n"));
|
|
9874
|
+
console.log(chalk14.dim(" Installing sync infrastructure...\n"));
|
|
9792
9875
|
const hookResult = installPreCommitHook();
|
|
9793
9876
|
if (hookResult.installed) {
|
|
9794
|
-
console.log(` ${chalk14.green("\u2713")} Pre-commit hook installed`);
|
|
9877
|
+
console.log(` ${chalk14.green("\u2713")} Pre-commit hook installed \u2014 configs sync on every commit`);
|
|
9795
9878
|
} else if (hookResult.alreadyInstalled) {
|
|
9796
9879
|
console.log(` ${chalk14.green("\u2713")} Pre-commit hook \u2014 active`);
|
|
9797
9880
|
}
|
|
@@ -9799,13 +9882,20 @@ async function initCommand(options) {
|
|
|
9799
9882
|
console.log(` ${chalk14.green("\u2713")} Onboarding hook \u2014 nudges new team members to set up`);
|
|
9800
9883
|
const { ensureBuiltinSkills: ensureBuiltinSkills2 } = await Promise.resolve().then(() => (init_builtin_skills(), builtin_skills_exports));
|
|
9801
9884
|
for (const agent of targetAgent) {
|
|
9802
|
-
if (agent === "claude" && !fs34.existsSync(".claude"))
|
|
9803
|
-
|
|
9804
|
-
if (agent === "
|
|
9885
|
+
if (agent === "claude" && !fs34.existsSync(".claude"))
|
|
9886
|
+
fs34.mkdirSync(".claude", { recursive: true });
|
|
9887
|
+
if (agent === "cursor" && !fs34.existsSync(".cursor"))
|
|
9888
|
+
fs34.mkdirSync(".cursor", { recursive: true });
|
|
9889
|
+
if (agent === "codex" && !fs34.existsSync(".agents"))
|
|
9890
|
+
fs34.mkdirSync(".agents", { recursive: true });
|
|
9805
9891
|
}
|
|
9806
9892
|
const skillsWritten = ensureBuiltinSkills2();
|
|
9807
9893
|
if (skillsWritten.length > 0) {
|
|
9808
|
-
console.log(
|
|
9894
|
+
console.log(
|
|
9895
|
+
` ${chalk14.green("\u2713")} Agent skills installed \u2014 /setup-caliber, /find-skills, /save-learning`
|
|
9896
|
+
);
|
|
9897
|
+
} else {
|
|
9898
|
+
console.log(` ${chalk14.green("\u2713")} Agent skills \u2014 already installed`);
|
|
9809
9899
|
}
|
|
9810
9900
|
const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
|
|
9811
9901
|
if (hasLearnableAgent) {
|
|
@@ -9815,15 +9905,31 @@ async function initCommand(options) {
|
|
|
9815
9905
|
trackInitLearnEnabled(true);
|
|
9816
9906
|
}
|
|
9817
9907
|
console.log("");
|
|
9908
|
+
console.log(chalk14.dim(" New team members can run /setup-caliber inside their coding agent"));
|
|
9909
|
+
console.log(chalk14.dim(" (Claude Code or Cursor) to get set up automatically.\n"));
|
|
9818
9910
|
const baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
9819
|
-
log(
|
|
9911
|
+
console.log(chalk14.dim(" Current config score:"));
|
|
9912
|
+
displayScoreSummary(baselineScore);
|
|
9913
|
+
if (options.verbose) {
|
|
9914
|
+
for (const c of baselineScore.checks) {
|
|
9915
|
+
log(
|
|
9916
|
+
options.verbose,
|
|
9917
|
+
` ${c.passed ? "\u2713" : "\u2717"} ${c.name}: ${c.earnedPoints}/${c.maxPoints}${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`
|
|
9918
|
+
);
|
|
9919
|
+
}
|
|
9920
|
+
}
|
|
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));
|
|
@@ -9837,15 +9943,27 @@ async function initCommand(options) {
|
|
|
9837
9943
|
]);
|
|
9838
9944
|
const passingCount = baselineScore.checks.filter((c) => c.passed).length;
|
|
9839
9945
|
const failingCount = baselineScore.checks.filter((c) => !c.passed).length;
|
|
9840
|
-
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
|
|
9841
9946
|
let skipGeneration = false;
|
|
9842
|
-
if (hasExistingConfig && baselineScore.score === 100
|
|
9843
|
-
|
|
9947
|
+
if (hasExistingConfig && baselineScore.score === 100) {
|
|
9948
|
+
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, true);
|
|
9949
|
+
console.log(chalk14.bold.green("\n Your config is already optimal.\n"));
|
|
9950
|
+
skipGeneration = !options.force;
|
|
9844
9951
|
} else if (hasExistingConfig && !options.force && !options.autoApprove) {
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9952
|
+
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
|
|
9953
|
+
console.log(
|
|
9954
|
+
chalk14.dim("\n Sync infrastructure is ready. Caliber can also audit your existing")
|
|
9955
|
+
);
|
|
9956
|
+
console.log(chalk14.dim(" configs and improve them using AI.\n"));
|
|
9957
|
+
const auditAnswer = await promptInput(" Audit and improve your existing config? (Y/n) ");
|
|
9958
|
+
skipGeneration = auditAnswer.toLowerCase() === "n";
|
|
9959
|
+
} else if (!hasExistingConfig && !options.force && !options.autoApprove) {
|
|
9960
|
+
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
|
|
9961
|
+
console.log(chalk14.dim("\n Sync infrastructure is ready. Caliber can also generate tailored"));
|
|
9962
|
+
console.log(chalk14.dim(" CLAUDE.md, Cursor rules, and Codex configs for your project.\n"));
|
|
9963
|
+
const generateAnswer = await promptInput(" Generate agent configs? (Y/n) ");
|
|
9964
|
+
skipGeneration = generateAnswer.toLowerCase() === "n";
|
|
9965
|
+
} else {
|
|
9966
|
+
trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
|
|
9849
9967
|
}
|
|
9850
9968
|
if (skipGeneration) {
|
|
9851
9969
|
const {
|
|
@@ -9873,7 +9991,12 @@ async function initCommand(options) {
|
|
|
9873
9991
|
if (targetAgent.includes("cursor")) {
|
|
9874
9992
|
const rulesDir = path28.join(".cursor", "rules");
|
|
9875
9993
|
if (!fs34.existsSync(rulesDir)) fs34.mkdirSync(rulesDir, { recursive: true });
|
|
9876
|
-
for (const rule of [
|
|
9994
|
+
for (const rule of [
|
|
9995
|
+
getCursorPreCommitRule2(),
|
|
9996
|
+
getCursorLearningsRule2(),
|
|
9997
|
+
getCursorSyncRule2(),
|
|
9998
|
+
getCursorSetupRule2()
|
|
9999
|
+
]) {
|
|
9877
10000
|
fs34.writeFileSync(path28.join(rulesDir, rule.filename), rule.content);
|
|
9878
10001
|
}
|
|
9879
10002
|
console.log(` ${chalk14.green("\u2713")} Cursor rules \u2014 added Caliber sync rules`);
|
|
@@ -9905,9 +10028,12 @@ async function initCommand(options) {
|
|
|
9905
10028
|
trackInitCompleted("sync-only", baselineScore.score);
|
|
9906
10029
|
console.log(chalk14.bold.green("\n Caliber sync is set up!\n"));
|
|
9907
10030
|
console.log(chalk14.dim(" Your agent configs will sync automatically on every commit."));
|
|
9908
|
-
console.log(
|
|
10031
|
+
console.log(
|
|
10032
|
+
chalk14.dim(" Run ") + title(`${bin} init --force`) + chalk14.dim(" anytime to generate or improve configs.\n")
|
|
10033
|
+
);
|
|
9909
10034
|
return;
|
|
9910
10035
|
}
|
|
10036
|
+
console.log(title.bold("\n Step 3/4 \u2014 Generate\n"));
|
|
9911
10037
|
const genModelInfo = fastModel ? ` Using ${displayModel} for docs, ${fastModel} for skills` : ` Using ${displayModel}`;
|
|
9912
10038
|
console.log(chalk14.dim(genModelInfo + "\n"));
|
|
9913
10039
|
if (report) report.markStep("Generation");
|
|
@@ -9923,8 +10049,14 @@ async function initCommand(options) {
|
|
|
9923
10049
|
const TASK_STACK = display.add("Detecting project stack", { pipelineLabel: "Scan" });
|
|
9924
10050
|
const TASK_CONFIG = display.add("Generating configs", { depth: 1, pipelineLabel: "Generate" });
|
|
9925
10051
|
const TASK_SKILLS_GEN = display.add("Generating skills", { depth: 2, pipelineLabel: "Skills" });
|
|
9926
|
-
const TASK_SKILLS_SEARCH = display.add("Searching community skills", {
|
|
9927
|
-
|
|
10052
|
+
const TASK_SKILLS_SEARCH = display.add("Searching community skills", {
|
|
10053
|
+
depth: 1,
|
|
10054
|
+
pipelineLabel: "Search",
|
|
10055
|
+
pipelineRow: 1
|
|
10056
|
+
});
|
|
10057
|
+
const TASK_SCORE_REFINE = display.add("Validating & refining config", {
|
|
10058
|
+
pipelineLabel: "Validate"
|
|
10059
|
+
});
|
|
9928
10060
|
display.start();
|
|
9929
10061
|
display.enableWaitingContent();
|
|
9930
10062
|
try {
|
|
@@ -9934,21 +10066,39 @@ async function initCommand(options) {
|
|
|
9934
10066
|
const stackSummary = stackParts.join(", ") || "no languages";
|
|
9935
10067
|
const largeRepoNote = fingerprint.fileTree.length > 5e3 ? ` (${fingerprint.fileTree.length.toLocaleString()} files, smart sampling active)` : "";
|
|
9936
10068
|
display.update(TASK_STACK, "done", stackSummary + largeRepoNote);
|
|
9937
|
-
trackInitProjectDiscovered(
|
|
9938
|
-
|
|
10069
|
+
trackInitProjectDiscovered(
|
|
10070
|
+
fingerprint.languages.length,
|
|
10071
|
+
fingerprint.frameworks.length,
|
|
10072
|
+
fingerprint.fileTree.length
|
|
10073
|
+
);
|
|
10074
|
+
log(
|
|
10075
|
+
options.verbose,
|
|
10076
|
+
`Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`
|
|
10077
|
+
);
|
|
9939
10078
|
const cliSources = options.source || [];
|
|
9940
10079
|
const workspaces = getDetectedWorkspaces(process.cwd());
|
|
9941
10080
|
const sources2 = resolveAllSources(process.cwd(), cliSources, workspaces);
|
|
9942
10081
|
if (sources2.length > 0) {
|
|
9943
10082
|
fingerprint.sources = sources2;
|
|
9944
|
-
log(
|
|
10083
|
+
log(
|
|
10084
|
+
options.verbose,
|
|
10085
|
+
`Sources: ${sources2.length} resolved (${sources2.map((s) => s.name).join(", ")})`
|
|
10086
|
+
);
|
|
9945
10087
|
}
|
|
9946
10088
|
if (report) {
|
|
9947
|
-
report.addJson("Fingerprint: Git", {
|
|
10089
|
+
report.addJson("Fingerprint: Git", {
|
|
10090
|
+
remote: fingerprint.gitRemoteUrl,
|
|
10091
|
+
packageName: fingerprint.packageName
|
|
10092
|
+
});
|
|
9948
10093
|
report.addCodeBlock("Fingerprint: File Tree", fingerprint.fileTree.join("\n"));
|
|
9949
|
-
report.addJson("Fingerprint: Detected Stack", {
|
|
10094
|
+
report.addJson("Fingerprint: Detected Stack", {
|
|
10095
|
+
languages: fingerprint.languages,
|
|
10096
|
+
frameworks: fingerprint.frameworks,
|
|
10097
|
+
tools: fingerprint.tools
|
|
10098
|
+
});
|
|
9950
10099
|
report.addJson("Fingerprint: Existing Configs", fingerprint.existingConfigs);
|
|
9951
|
-
if (fingerprint.codeAnalysis)
|
|
10100
|
+
if (fingerprint.codeAnalysis)
|
|
10101
|
+
report.addJson("Fingerprint: Code Analysis", fingerprint.codeAnalysis);
|
|
9952
10102
|
}
|
|
9953
10103
|
const isEmpty = fingerprint.fileTree.length < 3;
|
|
9954
10104
|
if (isEmpty) {
|
|
@@ -9979,13 +10129,26 @@ async function initCommand(options) {
|
|
|
9979
10129
|
let passingChecks;
|
|
9980
10130
|
let currentScore;
|
|
9981
10131
|
if (hasExistingConfig && localBaseline.score >= 95 && !options.force) {
|
|
9982
|
-
const currentLlmFixable = localBaseline.checks.filter(
|
|
9983
|
-
|
|
10132
|
+
const currentLlmFixable = localBaseline.checks.filter(
|
|
10133
|
+
(c) => !c.passed && c.maxPoints > 0 && !NON_LLM_CHECKS.has(c.id)
|
|
10134
|
+
);
|
|
10135
|
+
failingChecks = currentLlmFixable.map((c) => ({
|
|
10136
|
+
name: c.name,
|
|
10137
|
+
suggestion: c.suggestion,
|
|
10138
|
+
fix: c.fix
|
|
10139
|
+
}));
|
|
9984
10140
|
passingChecks = localBaseline.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
|
|
9985
10141
|
currentScore = localBaseline.score;
|
|
9986
10142
|
}
|
|
9987
10143
|
if (report) {
|
|
9988
|
-
const fullPrompt = buildGeneratePrompt(
|
|
10144
|
+
const fullPrompt = buildGeneratePrompt(
|
|
10145
|
+
fingerprint,
|
|
10146
|
+
targetAgent,
|
|
10147
|
+
fingerprint.description,
|
|
10148
|
+
failingChecks,
|
|
10149
|
+
currentScore,
|
|
10150
|
+
passingChecks
|
|
10151
|
+
);
|
|
9989
10152
|
report.addCodeBlock("Generation: Full LLM Prompt", fullPrompt);
|
|
9990
10153
|
}
|
|
9991
10154
|
const result = await generateSetup(
|
|
@@ -10105,8 +10268,11 @@ async function initCommand(options) {
|
|
|
10105
10268
|
if (rawOutput) report.addCodeBlock("Generation: Raw LLM Response", rawOutput);
|
|
10106
10269
|
report.addJson("Generation: Parsed Config", generatedSetup);
|
|
10107
10270
|
}
|
|
10108
|
-
log(
|
|
10109
|
-
|
|
10271
|
+
log(
|
|
10272
|
+
options.verbose,
|
|
10273
|
+
`Generation completed: ${elapsedMs}ms, stopReason: ${genStopReason || "end_turn"}`
|
|
10274
|
+
);
|
|
10275
|
+
console.log(title.bold(" Step 4/4 \u2014 Finalize\n"));
|
|
10110
10276
|
const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
|
|
10111
10277
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
10112
10278
|
const totalChanges = staged.newFiles + staged.modifiedFiles;
|
|
@@ -10117,10 +10283,18 @@ async function initCommand(options) {
|
|
|
10117
10283
|
}
|
|
10118
10284
|
console.log("");
|
|
10119
10285
|
}
|
|
10120
|
-
console.log(
|
|
10286
|
+
console.log(
|
|
10287
|
+
chalk14.dim(
|
|
10288
|
+
` ${chalk14.green(`${staged.newFiles} new`)} / ${chalk14.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}`
|
|
10289
|
+
)
|
|
10290
|
+
);
|
|
10121
10291
|
if (skillSearchResult.results.length > 0) {
|
|
10122
|
-
console.log(
|
|
10123
|
-
|
|
10292
|
+
console.log(
|
|
10293
|
+
chalk14.dim(
|
|
10294
|
+
` ${chalk14.cyan(`${skillSearchResult.results.length}`)} community skills available to install
|
|
10295
|
+
`
|
|
10296
|
+
)
|
|
10297
|
+
);
|
|
10124
10298
|
} else {
|
|
10125
10299
|
console.log("");
|
|
10126
10300
|
}
|
|
@@ -10156,8 +10330,12 @@ async function initCommand(options) {
|
|
|
10156
10330
|
}
|
|
10157
10331
|
const updatedFiles = collectSetupFiles(generatedSetup, targetAgent);
|
|
10158
10332
|
const restaged = stageFiles(updatedFiles, process.cwd());
|
|
10159
|
-
console.log(
|
|
10160
|
-
|
|
10333
|
+
console.log(
|
|
10334
|
+
chalk14.dim(
|
|
10335
|
+
` ${chalk14.green(`${restaged.newFiles} new`)} / ${chalk14.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
|
|
10336
|
+
`
|
|
10337
|
+
)
|
|
10338
|
+
);
|
|
10161
10339
|
printSetupSummary(generatedSetup);
|
|
10162
10340
|
const { openReview: openRev } = await Promise.resolve().then(() => (init_review(), review_exports));
|
|
10163
10341
|
await openRev("terminal", restaged.stagedFiles);
|
|
@@ -10183,7 +10361,8 @@ async function initCommand(options) {
|
|
|
10183
10361
|
const agentRefs = [];
|
|
10184
10362
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
10185
10363
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
10186
|
-
if (agentRefs.length === 0)
|
|
10364
|
+
if (agentRefs.length === 0)
|
|
10365
|
+
agentRefs.push("See CLAUDE.md and .cursor/rules/ for agent configurations.");
|
|
10187
10366
|
const stubContent = `# AGENTS.md
|
|
10188
10367
|
|
|
10189
10368
|
This project uses AI coding agents configured by [Caliber](https://github.com/caliber-ai-org/ai-setup).
|
|
@@ -10230,24 +10409,39 @@ ${agentRefs.join(" ")}
|
|
|
10230
10409
|
if (afterScore.score < baselineScore.score) {
|
|
10231
10410
|
trackInitScoreRegression(baselineScore.score, afterScore.score);
|
|
10232
10411
|
console.log("");
|
|
10233
|
-
console.log(
|
|
10412
|
+
console.log(
|
|
10413
|
+
chalk14.yellow(
|
|
10414
|
+
` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`
|
|
10415
|
+
)
|
|
10416
|
+
);
|
|
10234
10417
|
try {
|
|
10235
10418
|
const { restored, removed } = undoSetup();
|
|
10236
10419
|
if (restored.length > 0 || removed.length > 0) {
|
|
10237
|
-
console.log(
|
|
10420
|
+
console.log(
|
|
10421
|
+
chalk14.dim(
|
|
10422
|
+
` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`
|
|
10423
|
+
)
|
|
10424
|
+
);
|
|
10238
10425
|
}
|
|
10239
10426
|
} catch {
|
|
10240
10427
|
}
|
|
10241
|
-
console.log(
|
|
10428
|
+
console.log(
|
|
10429
|
+
chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to override.\n")
|
|
10430
|
+
);
|
|
10242
10431
|
return;
|
|
10243
10432
|
}
|
|
10244
10433
|
if (report) {
|
|
10245
10434
|
report.markStep("Post-write scoring");
|
|
10246
|
-
report.addSection(
|
|
10435
|
+
report.addSection(
|
|
10436
|
+
"Scoring: Post-Write",
|
|
10437
|
+
`**Score**: ${afterScore.score}/100 (delta: ${afterScore.score - baselineScore.score >= 0 ? "+" : ""}${afterScore.score - baselineScore.score})
|
|
10247
10438
|
|
|
10248
10439
|
| Check | Passed | Points | Max |
|
|
10249
10440
|
|-------|--------|--------|-----|
|
|
10250
|
-
` + afterScore.checks.map(
|
|
10441
|
+
` + afterScore.checks.map(
|
|
10442
|
+
(c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`
|
|
10443
|
+
).join("\n")
|
|
10444
|
+
);
|
|
10251
10445
|
}
|
|
10252
10446
|
recordScore(afterScore, "init");
|
|
10253
10447
|
trackInitCompleted("full-generation", afterScore.score);
|
|
@@ -10255,7 +10449,10 @@ ${agentRefs.join(" ")}
|
|
|
10255
10449
|
if (options.verbose) {
|
|
10256
10450
|
log(options.verbose, `Final score: ${afterScore.score}/100`);
|
|
10257
10451
|
for (const c of afterScore.checks.filter((ch) => !ch.passed)) {
|
|
10258
|
-
log(
|
|
10452
|
+
log(
|
|
10453
|
+
options.verbose,
|
|
10454
|
+
` Still failing: ${c.name} (${c.earnedPoints}/${c.maxPoints})${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`
|
|
10455
|
+
);
|
|
10259
10456
|
}
|
|
10260
10457
|
}
|
|
10261
10458
|
let communitySkillsInstalled = 0;
|
|
@@ -10272,21 +10469,32 @@ ${agentRefs.join(" ")}
|
|
|
10272
10469
|
const done = chalk14.green("\u2713");
|
|
10273
10470
|
console.log(chalk14.bold.green("\n Caliber is set up!\n"));
|
|
10274
10471
|
console.log(chalk14.bold(" What's configured:\n"));
|
|
10275
|
-
console.log(
|
|
10276
|
-
|
|
10277
|
-
|
|
10472
|
+
console.log(
|
|
10473
|
+
` ${done} Continuous sync ${chalk14.dim("pre-commit hook keeps all agent configs in sync")}`
|
|
10474
|
+
);
|
|
10475
|
+
console.log(
|
|
10476
|
+
` ${done} Config generated ${title(`${bin} score`)} ${chalk14.dim("for full breakdown")}`
|
|
10477
|
+
);
|
|
10478
|
+
console.log(
|
|
10479
|
+
` ${done} Agent skills ${chalk14.dim("/setup-caliber for new team members")}`
|
|
10480
|
+
);
|
|
10278
10481
|
if (hasLearnableAgent) {
|
|
10279
|
-
console.log(
|
|
10482
|
+
console.log(
|
|
10483
|
+
` ${done} Session learning ${chalk14.dim("agent learns from your feedback")}`
|
|
10484
|
+
);
|
|
10280
10485
|
}
|
|
10281
10486
|
if (communitySkillsInstalled > 0) {
|
|
10282
|
-
console.log(
|
|
10487
|
+
console.log(
|
|
10488
|
+
` ${done} Community skills ${chalk14.dim(`${communitySkillsInstalled} skill${communitySkillsInstalled > 1 ? "s" : ""} installed for your stack`)}`
|
|
10489
|
+
);
|
|
10283
10490
|
}
|
|
10284
10491
|
console.log(chalk14.bold("\n What happens next:\n"));
|
|
10285
|
-
console.log(chalk14.dim(" Every commit
|
|
10286
|
-
console.log(chalk14.dim(" New team members run /setup-caliber to get set up instantly.\n"));
|
|
10287
|
-
console.log(
|
|
10288
|
-
console.log(` ${title(`${bin}
|
|
10289
|
-
console.log(` ${title(`${bin}
|
|
10492
|
+
console.log(chalk14.dim(" Every commit will automatically sync your agent configs."));
|
|
10493
|
+
console.log(chalk14.dim(" New team members can run /setup-caliber to get set up instantly.\n"));
|
|
10494
|
+
console.log(chalk14.bold(" Explore:\n"));
|
|
10495
|
+
console.log(` ${title(`${bin} score`)} Full scoring breakdown with improvement tips`);
|
|
10496
|
+
console.log(` ${title(`${bin} skills`)} Find community skills for your stack`);
|
|
10497
|
+
console.log(` ${title(`${bin} undo`)} Revert all changes from this run`);
|
|
10290
10498
|
console.log(` ${title(`${bin} uninstall`)} Remove Caliber completely`);
|
|
10291
10499
|
console.log("");
|
|
10292
10500
|
if (options.showTokens) {
|
|
@@ -10296,8 +10504,10 @@ ${agentRefs.join(" ")}
|
|
|
10296
10504
|
report.markStep("Finished");
|
|
10297
10505
|
const reportPath = path28.join(process.cwd(), ".caliber", "debug-report.md");
|
|
10298
10506
|
report.write(reportPath);
|
|
10299
|
-
console.log(
|
|
10300
|
-
`))
|
|
10507
|
+
console.log(
|
|
10508
|
+
chalk14.dim(` Debug report written to ${path28.relative(process.cwd(), reportPath)}
|
|
10509
|
+
`)
|
|
10510
|
+
);
|
|
10301
10511
|
}
|
|
10302
10512
|
}
|
|
10303
10513
|
|
|
@@ -10660,12 +10870,23 @@ import { execSync as execSync15 } from "child_process";
|
|
|
10660
10870
|
var MAX_DIFF_BYTES = 1e5;
|
|
10661
10871
|
var DOC_PATTERNS = [
|
|
10662
10872
|
"CLAUDE.md",
|
|
10873
|
+
"AGENTS.md",
|
|
10663
10874
|
"README.md",
|
|
10664
10875
|
".cursorrules",
|
|
10665
10876
|
".cursor/rules/",
|
|
10877
|
+
".cursor/skills/",
|
|
10666
10878
|
".claude/skills/",
|
|
10879
|
+
".agents/skills/",
|
|
10880
|
+
".opencode/skills/",
|
|
10881
|
+
".github/copilot-instructions.md",
|
|
10882
|
+
".github/instructions/",
|
|
10667
10883
|
"CALIBER_LEARNINGS.md"
|
|
10668
10884
|
];
|
|
10885
|
+
function truncateAtLineEnd(text, maxBytes) {
|
|
10886
|
+
if (text.length <= maxBytes) return text;
|
|
10887
|
+
const lastNewline = text.lastIndexOf("\n", maxBytes);
|
|
10888
|
+
return lastNewline === -1 ? text.slice(0, maxBytes) : text.slice(0, lastNewline);
|
|
10889
|
+
}
|
|
10669
10890
|
function excludeArgs() {
|
|
10670
10891
|
return DOC_PATTERNS.flatMap((p) => ["--", `:!${p}`]);
|
|
10671
10892
|
}
|
|
@@ -10714,9 +10935,9 @@ function collectDiff(lastSha) {
|
|
|
10714
10935
|
const totalSize = committedDiff.length + stagedDiff.length + unstagedDiff.length;
|
|
10715
10936
|
if (totalSize > MAX_DIFF_BYTES) {
|
|
10716
10937
|
const ratio = MAX_DIFF_BYTES / totalSize;
|
|
10717
|
-
committedDiff = committedDiff
|
|
10718
|
-
stagedDiff = stagedDiff
|
|
10719
|
-
unstagedDiff = unstagedDiff
|
|
10938
|
+
committedDiff = truncateAtLineEnd(committedDiff, Math.floor(committedDiff.length * ratio));
|
|
10939
|
+
stagedDiff = truncateAtLineEnd(stagedDiff, Math.floor(stagedDiff.length * ratio));
|
|
10940
|
+
unstagedDiff = truncateAtLineEnd(unstagedDiff, Math.floor(unstagedDiff.length * ratio));
|
|
10720
10941
|
}
|
|
10721
10942
|
const hasChanges = !!(committedDiff || stagedDiff || unstagedDiff || changedFiles.length);
|
|
10722
10943
|
const parts = [];
|
|
@@ -10735,11 +10956,11 @@ import path30 from "path";
|
|
|
10735
10956
|
function writeRefreshDocs(docs) {
|
|
10736
10957
|
const written = [];
|
|
10737
10958
|
if (docs.agentsMd) {
|
|
10738
|
-
fs37.writeFileSync("AGENTS.md",
|
|
10959
|
+
fs37.writeFileSync("AGENTS.md", appendManagedBlocks(docs.agentsMd, "codex"));
|
|
10739
10960
|
written.push("AGENTS.md");
|
|
10740
10961
|
}
|
|
10741
10962
|
if (docs.claudeMd) {
|
|
10742
|
-
fs37.writeFileSync("CLAUDE.md",
|
|
10963
|
+
fs37.writeFileSync("CLAUDE.md", appendManagedBlocks(docs.claudeMd));
|
|
10743
10964
|
written.push("CLAUDE.md");
|
|
10744
10965
|
}
|
|
10745
10966
|
if (docs.readmeMd) {
|
|
@@ -10758,17 +10979,12 @@ function writeRefreshDocs(docs) {
|
|
|
10758
10979
|
written.push(`.cursor/rules/${rule.filename}`);
|
|
10759
10980
|
}
|
|
10760
10981
|
}
|
|
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
10982
|
if (docs.copilotInstructions) {
|
|
10770
10983
|
fs37.mkdirSync(".github", { recursive: true });
|
|
10771
|
-
fs37.writeFileSync(
|
|
10984
|
+
fs37.writeFileSync(
|
|
10985
|
+
path30.join(".github", "copilot-instructions.md"),
|
|
10986
|
+
appendManagedBlocks(docs.copilotInstructions, "copilot")
|
|
10987
|
+
);
|
|
10772
10988
|
written.push(".github/copilot-instructions.md");
|
|
10773
10989
|
}
|
|
10774
10990
|
if (docs.copilotInstructionFiles) {
|
|
@@ -10784,6 +11000,7 @@ function writeRefreshDocs(docs) {
|
|
|
10784
11000
|
|
|
10785
11001
|
// src/ai/refresh.ts
|
|
10786
11002
|
init_config();
|
|
11003
|
+
init_pre_commit_block();
|
|
10787
11004
|
async function refreshDocs(diff, existingDocs, projectContext, learnedSection, sources2) {
|
|
10788
11005
|
const prompt = buildRefreshPrompt(diff, existingDocs, projectContext, learnedSection, sources2);
|
|
10789
11006
|
const fastModel = getFastModel();
|
|
@@ -10799,13 +11016,17 @@ function buildRefreshPrompt(diff, existingDocs, projectContext, learnedSection,
|
|
|
10799
11016
|
const parts = [];
|
|
10800
11017
|
parts.push("Update documentation based on the following code changes.\n");
|
|
10801
11018
|
if (projectContext.packageName) parts.push(`Project: ${projectContext.packageName}`);
|
|
10802
|
-
if (projectContext.languages?.length)
|
|
10803
|
-
|
|
11019
|
+
if (projectContext.languages?.length)
|
|
11020
|
+
parts.push(`Languages: ${projectContext.languages.join(", ")}`);
|
|
11021
|
+
if (projectContext.frameworks?.length)
|
|
11022
|
+
parts.push(`Frameworks: ${projectContext.frameworks.join(", ")}`);
|
|
10804
11023
|
if (projectContext.fileTree?.length) {
|
|
10805
11024
|
const tree = projectContext.fileTree.slice(0, 200);
|
|
10806
|
-
parts.push(
|
|
11025
|
+
parts.push(
|
|
11026
|
+
`
|
|
10807
11027
|
File tree (${tree.length}/${projectContext.fileTree.length} \u2014 only reference paths from this list):
|
|
10808
|
-
${tree.join("\n")}`
|
|
11028
|
+
${tree.join("\n")}`
|
|
11029
|
+
);
|
|
10809
11030
|
}
|
|
10810
11031
|
parts.push(`
|
|
10811
11032
|
Changed files: ${diff.changedFiles.join(", ")}`);
|
|
@@ -10825,11 +11046,11 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
10825
11046
|
parts.push("\n--- Current Documentation ---");
|
|
10826
11047
|
if (existingDocs.agentsMd) {
|
|
10827
11048
|
parts.push("\n[AGENTS.md]");
|
|
10828
|
-
parts.push(existingDocs.agentsMd);
|
|
11049
|
+
parts.push(stripManagedBlocks(existingDocs.agentsMd));
|
|
10829
11050
|
}
|
|
10830
11051
|
if (existingDocs.claudeMd) {
|
|
10831
11052
|
parts.push("\n[CLAUDE.md]");
|
|
10832
|
-
parts.push(existingDocs.claudeMd);
|
|
11053
|
+
parts.push(stripManagedBlocks(existingDocs.claudeMd));
|
|
10833
11054
|
}
|
|
10834
11055
|
if (existingDocs.readmeMd) {
|
|
10835
11056
|
parts.push("\n[README.md]");
|
|
@@ -10848,11 +11069,23 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
10848
11069
|
}
|
|
10849
11070
|
if (existingDocs.cursorRules?.length) {
|
|
10850
11071
|
for (const rule of existingDocs.cursorRules) {
|
|
11072
|
+
if (rule.filename.startsWith("caliber-")) continue;
|
|
10851
11073
|
parts.push(`
|
|
10852
11074
|
[.cursor/rules/${rule.filename}]`);
|
|
10853
11075
|
parts.push(rule.content);
|
|
10854
11076
|
}
|
|
10855
11077
|
}
|
|
11078
|
+
if (existingDocs.copilotInstructions) {
|
|
11079
|
+
parts.push("\n[.github/copilot-instructions.md]");
|
|
11080
|
+
parts.push(stripManagedBlocks(existingDocs.copilotInstructions));
|
|
11081
|
+
}
|
|
11082
|
+
if (existingDocs.copilotInstructionFiles?.length) {
|
|
11083
|
+
for (const file of existingDocs.copilotInstructionFiles) {
|
|
11084
|
+
parts.push(`
|
|
11085
|
+
[.github/instructions/${file.filename}]`);
|
|
11086
|
+
parts.push(file.content);
|
|
11087
|
+
}
|
|
11088
|
+
}
|
|
10856
11089
|
if (learnedSection) {
|
|
10857
11090
|
parts.push("\n--- Learned Patterns (from session learning) ---");
|
|
10858
11091
|
parts.push("Consider these accumulated learnings when deciding what to update:");
|
|
@@ -11062,6 +11295,49 @@ function migrateInlineLearnings() {
|
|
|
11062
11295
|
init_config();
|
|
11063
11296
|
init_resolve_caliber();
|
|
11064
11297
|
init_builtin_skills();
|
|
11298
|
+
function writeRefreshError(error) {
|
|
11299
|
+
try {
|
|
11300
|
+
if (!fs40.existsSync(CALIBER_DIR)) fs40.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
11301
|
+
fs40.writeFileSync(
|
|
11302
|
+
REFRESH_LAST_ERROR_FILE,
|
|
11303
|
+
JSON.stringify(
|
|
11304
|
+
{
|
|
11305
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11306
|
+
error: error instanceof Error ? error.message : String(error),
|
|
11307
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
11308
|
+
cwd: process.cwd(),
|
|
11309
|
+
nodeVersion: process.version
|
|
11310
|
+
},
|
|
11311
|
+
null,
|
|
11312
|
+
2
|
|
11313
|
+
)
|
|
11314
|
+
);
|
|
11315
|
+
} catch {
|
|
11316
|
+
}
|
|
11317
|
+
}
|
|
11318
|
+
function readRefreshError() {
|
|
11319
|
+
try {
|
|
11320
|
+
if (!fs40.existsSync(REFRESH_LAST_ERROR_FILE)) return null;
|
|
11321
|
+
return JSON.parse(fs40.readFileSync(REFRESH_LAST_ERROR_FILE, "utf-8"));
|
|
11322
|
+
} catch {
|
|
11323
|
+
return null;
|
|
11324
|
+
}
|
|
11325
|
+
}
|
|
11326
|
+
function clearRefreshError() {
|
|
11327
|
+
try {
|
|
11328
|
+
if (fs40.existsSync(REFRESH_LAST_ERROR_FILE)) fs40.unlinkSync(REFRESH_LAST_ERROR_FILE);
|
|
11329
|
+
} catch {
|
|
11330
|
+
}
|
|
11331
|
+
}
|
|
11332
|
+
function detectSyncedAgents(writtenFiles) {
|
|
11333
|
+
const agents = [];
|
|
11334
|
+
const joined = writtenFiles.join(" ");
|
|
11335
|
+
if (joined.includes("CLAUDE.md") || joined.includes(".claude/")) agents.push("Claude Code");
|
|
11336
|
+
if (joined.includes(".cursor/") || joined.includes(".cursorrules")) agents.push("Cursor");
|
|
11337
|
+
if (joined.includes("copilot-instructions") || joined.includes(".github/instructions/")) agents.push("Copilot");
|
|
11338
|
+
if (joined.includes("AGENTS.md") || joined.includes(".agents/")) agents.push("Codex");
|
|
11339
|
+
return agents;
|
|
11340
|
+
}
|
|
11065
11341
|
function log2(quiet, ...args) {
|
|
11066
11342
|
if (!quiet) console.log(...args);
|
|
11067
11343
|
}
|
|
@@ -11080,21 +11356,41 @@ function discoverGitRepos(parentDir) {
|
|
|
11080
11356
|
}
|
|
11081
11357
|
return repos.sort();
|
|
11082
11358
|
}
|
|
11359
|
+
function collectFilesToWrite(updatedDocs) {
|
|
11360
|
+
const files = [];
|
|
11361
|
+
if (updatedDocs.agentsMd) files.push("AGENTS.md");
|
|
11362
|
+
if (updatedDocs.claudeMd) files.push("CLAUDE.md");
|
|
11363
|
+
if (updatedDocs.readmeMd) files.push("README.md");
|
|
11364
|
+
if (updatedDocs.cursorrules) files.push(".cursorrules");
|
|
11365
|
+
if (Array.isArray(updatedDocs.cursorRules)) {
|
|
11366
|
+
for (const r of updatedDocs.cursorRules)
|
|
11367
|
+
files.push(`.cursor/rules/${r.filename}`);
|
|
11368
|
+
}
|
|
11369
|
+
if (updatedDocs.copilotInstructions) files.push(".github/copilot-instructions.md");
|
|
11370
|
+
if (Array.isArray(updatedDocs.copilotInstructionFiles)) {
|
|
11371
|
+
for (const f of updatedDocs.copilotInstructionFiles)
|
|
11372
|
+
files.push(`.github/instructions/${f.filename}`);
|
|
11373
|
+
}
|
|
11374
|
+
return files;
|
|
11375
|
+
}
|
|
11083
11376
|
var REFRESH_COOLDOWN_MS = 3e4;
|
|
11084
11377
|
async function refreshSingleRepo(repoDir, options) {
|
|
11085
11378
|
const quiet = !!options.quiet;
|
|
11086
11379
|
const prefix = options.label ? `${chalk19.bold(options.label)} ` : "";
|
|
11087
11380
|
const state = readState();
|
|
11088
11381
|
const lastSha = state?.lastRefreshSha ?? null;
|
|
11089
|
-
|
|
11382
|
+
const currentSha = getCurrentHeadSha();
|
|
11383
|
+
if (state?.lastRefreshTimestamp && lastSha && currentSha === lastSha) {
|
|
11090
11384
|
const elapsed = Date.now() - new Date(state.lastRefreshTimestamp).getTime();
|
|
11091
11385
|
if (elapsed < REFRESH_COOLDOWN_MS && elapsed > 0) {
|
|
11092
|
-
log2(
|
|
11386
|
+
log2(
|
|
11387
|
+
quiet,
|
|
11388
|
+
chalk19.dim(`${prefix}Skipped \u2014 last refresh was ${Math.round(elapsed / 1e3)}s ago.`)
|
|
11389
|
+
);
|
|
11093
11390
|
return;
|
|
11094
11391
|
}
|
|
11095
11392
|
}
|
|
11096
11393
|
const diff = collectDiff(lastSha);
|
|
11097
|
-
const currentSha = getCurrentHeadSha();
|
|
11098
11394
|
if (!diff.hasChanges) {
|
|
11099
11395
|
if (currentSha) {
|
|
11100
11396
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -11103,9 +11399,9 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11103
11399
|
return;
|
|
11104
11400
|
}
|
|
11105
11401
|
const spinner = quiet ? null : ora6(`${prefix}Analyzing changes...`).start();
|
|
11106
|
-
const existingDocs = readExistingConfigs(repoDir);
|
|
11107
11402
|
const learnedSection = readLearnedSection();
|
|
11108
11403
|
const fingerprint = await collectFingerprint(repoDir);
|
|
11404
|
+
const existingDocs = fingerprint.existingConfigs;
|
|
11109
11405
|
const projectContext = {
|
|
11110
11406
|
languages: fingerprint.languages,
|
|
11111
11407
|
frameworks: fingerprint.frameworks,
|
|
@@ -11124,12 +11420,24 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11124
11420
|
const sourcesPayload = sources2.length > 0 ? sources2 : void 0;
|
|
11125
11421
|
let response;
|
|
11126
11422
|
try {
|
|
11127
|
-
response = await refreshDocs(
|
|
11423
|
+
response = await refreshDocs(
|
|
11424
|
+
diffPayload,
|
|
11425
|
+
existingDocs,
|
|
11426
|
+
projectContext,
|
|
11427
|
+
learnedSection,
|
|
11428
|
+
sourcesPayload
|
|
11429
|
+
);
|
|
11128
11430
|
} catch (firstErr) {
|
|
11129
11431
|
const isTransient = firstErr instanceof Error && TRANSIENT_ERRORS.some((e) => firstErr.message.toLowerCase().includes(e.toLowerCase()));
|
|
11130
11432
|
if (!isTransient) throw firstErr;
|
|
11131
11433
|
try {
|
|
11132
|
-
response = await refreshDocs(
|
|
11434
|
+
response = await refreshDocs(
|
|
11435
|
+
diffPayload,
|
|
11436
|
+
existingDocs,
|
|
11437
|
+
projectContext,
|
|
11438
|
+
learnedSection,
|
|
11439
|
+
sourcesPayload
|
|
11440
|
+
);
|
|
11133
11441
|
} catch {
|
|
11134
11442
|
spinner?.fail(`${prefix}Refresh failed after retry`);
|
|
11135
11443
|
throw firstErr;
|
|
@@ -11155,9 +11463,9 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11155
11463
|
}
|
|
11156
11464
|
const targetAgent = state?.targetAgent ?? detectTargetAgent(repoDir);
|
|
11157
11465
|
const preScore = computeLocalScore(repoDir, targetAgent);
|
|
11158
|
-
const
|
|
11466
|
+
const allFilesToWrite = collectFilesToWrite(response.updatedDocs);
|
|
11159
11467
|
const preRefreshContents = /* @__PURE__ */ new Map();
|
|
11160
|
-
for (const filePath of
|
|
11468
|
+
for (const filePath of allFilesToWrite) {
|
|
11161
11469
|
const fullPath = path33.resolve(repoDir, filePath);
|
|
11162
11470
|
try {
|
|
11163
11471
|
preRefreshContents.set(filePath, fs40.readFileSync(fullPath, "utf-8"));
|
|
@@ -11181,7 +11489,9 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11181
11489
|
fs40.writeFileSync(fullPath, content);
|
|
11182
11490
|
}
|
|
11183
11491
|
}
|
|
11184
|
-
spinner?.warn(
|
|
11492
|
+
spinner?.warn(
|
|
11493
|
+
`${prefix}Refresh reverted \u2014 score would drop from ${preScore.score} to ${postScore.score}`
|
|
11494
|
+
);
|
|
11185
11495
|
log2(quiet, chalk19.dim(` Config quality gate prevented a regression. No files were changed.`));
|
|
11186
11496
|
if (currentSha) {
|
|
11187
11497
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -11190,8 +11500,18 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11190
11500
|
}
|
|
11191
11501
|
recordScore(postScore, "refresh");
|
|
11192
11502
|
spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
|
|
11503
|
+
const fileChangesMap = new Map(
|
|
11504
|
+
(response.fileChanges || []).map((fc) => [fc.file, fc.description])
|
|
11505
|
+
);
|
|
11193
11506
|
for (const file of written) {
|
|
11194
|
-
|
|
11507
|
+
const desc = fileChangesMap.get(file);
|
|
11508
|
+
const suffix = desc ? chalk19.dim(` \u2014 ${desc}`) : "";
|
|
11509
|
+
log2(quiet, ` ${chalk19.green("\u2713")} ${file}${suffix}`);
|
|
11510
|
+
}
|
|
11511
|
+
const agents = detectSyncedAgents(written);
|
|
11512
|
+
if (agents.length > 1) {
|
|
11513
|
+
log2(quiet, chalk19.cyan(`
|
|
11514
|
+
${agents.length} agent formats in sync (${agents.join(", ")})`));
|
|
11195
11515
|
}
|
|
11196
11516
|
if (response.changesSummary) {
|
|
11197
11517
|
log2(quiet, chalk19.dim(`
|
|
@@ -11201,6 +11521,7 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
11201
11521
|
for (const file of builtinWritten) {
|
|
11202
11522
|
log2(quiet, ` ${chalk19.green("\u2713")} ${file} ${chalk19.dim("(built-in)")}`);
|
|
11203
11523
|
}
|
|
11524
|
+
clearRefreshError();
|
|
11204
11525
|
if (currentSha) {
|
|
11205
11526
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
11206
11527
|
}
|
|
@@ -11211,11 +11532,28 @@ async function refreshCommand(options) {
|
|
|
11211
11532
|
const { isCaliberRunning: isCaliberRunning2 } = await Promise.resolve().then(() => (init_lock(), lock_exports));
|
|
11212
11533
|
if (isCaliberRunning2()) return;
|
|
11213
11534
|
}
|
|
11535
|
+
if (!quiet) {
|
|
11536
|
+
const lastError = readRefreshError();
|
|
11537
|
+
if (lastError) {
|
|
11538
|
+
console.log(chalk19.yellow(`
|
|
11539
|
+
\u26A0 Last refresh failed (${lastError.timestamp}):`));
|
|
11540
|
+
console.log(chalk19.dim(` ${lastError.error}`));
|
|
11541
|
+
console.log(
|
|
11542
|
+
chalk19.dim(
|
|
11543
|
+
` Run with --debug for full details, or report at https://github.com/caliber-ai-org/ai-setup/issues
|
|
11544
|
+
`
|
|
11545
|
+
)
|
|
11546
|
+
);
|
|
11547
|
+
clearRefreshError();
|
|
11548
|
+
}
|
|
11549
|
+
}
|
|
11214
11550
|
try {
|
|
11215
11551
|
const config = loadConfig();
|
|
11216
11552
|
if (!config) {
|
|
11217
11553
|
if (quiet) return;
|
|
11218
|
-
console.log(
|
|
11554
|
+
console.log(
|
|
11555
|
+
chalk19.red("No LLM provider configured. Run ") + chalk19.hex("#83D1EB")(`${resolveCaliber()} config`) + chalk19.red(" (e.g. choose Cursor) or set an API key.")
|
|
11556
|
+
);
|
|
11219
11557
|
throw new Error("__exit__");
|
|
11220
11558
|
}
|
|
11221
11559
|
await validateModel({ fast: true });
|
|
@@ -11226,7 +11564,9 @@ async function refreshCommand(options) {
|
|
|
11226
11564
|
const repos = discoverGitRepos(process.cwd());
|
|
11227
11565
|
if (repos.length === 0) {
|
|
11228
11566
|
if (quiet) return;
|
|
11229
|
-
console.log(
|
|
11567
|
+
console.log(
|
|
11568
|
+
chalk19.red("Not inside a git repository and no git repos found in child directories.")
|
|
11569
|
+
);
|
|
11230
11570
|
throw new Error("__exit__");
|
|
11231
11571
|
}
|
|
11232
11572
|
log2(quiet, chalk19.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
|
|
@@ -11239,12 +11579,19 @@ async function refreshCommand(options) {
|
|
|
11239
11579
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
11240
11580
|
} catch (err) {
|
|
11241
11581
|
if (err instanceof Error && err.message === "__exit__") continue;
|
|
11242
|
-
|
|
11582
|
+
writeRefreshError(err);
|
|
11583
|
+
log2(
|
|
11584
|
+
quiet,
|
|
11585
|
+
chalk19.yellow(
|
|
11586
|
+
`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`
|
|
11587
|
+
)
|
|
11588
|
+
);
|
|
11243
11589
|
}
|
|
11244
11590
|
}
|
|
11245
11591
|
process.chdir(originalDir);
|
|
11246
11592
|
} catch (err) {
|
|
11247
11593
|
if (err instanceof Error && err.message === "__exit__") throw err;
|
|
11594
|
+
writeRefreshError(err);
|
|
11248
11595
|
if (quiet) return;
|
|
11249
11596
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
11250
11597
|
console.log(chalk19.red(`Refresh failed: ${msg}`));
|
|
@@ -11589,10 +11936,10 @@ function writeState2(state) {
|
|
|
11589
11936
|
function resetState() {
|
|
11590
11937
|
writeState2({ ...DEFAULT_STATE });
|
|
11591
11938
|
}
|
|
11592
|
-
var
|
|
11939
|
+
var LOCK_FILE = "finalize.lock";
|
|
11593
11940
|
var LOCK_STALE_MS = 5 * 60 * 1e3;
|
|
11594
11941
|
function lockFilePath() {
|
|
11595
|
-
return path34.join(getLearningDir(),
|
|
11942
|
+
return path34.join(getLearningDir(), LOCK_FILE);
|
|
11596
11943
|
}
|
|
11597
11944
|
function acquireFinalizeLock() {
|
|
11598
11945
|
ensureLearningDir();
|