@rely-ai/caliber 1.18.9 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +372 -132
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -177,13 +177,13 @@ __export(lock_exports, {
|
|
|
177
177
|
isCaliberRunning: () => isCaliberRunning,
|
|
178
178
|
releaseLock: () => releaseLock
|
|
179
179
|
});
|
|
180
|
-
import
|
|
181
|
-
import
|
|
180
|
+
import fs28 from "fs";
|
|
181
|
+
import path22 from "path";
|
|
182
182
|
import os4 from "os";
|
|
183
183
|
function isCaliberRunning() {
|
|
184
184
|
try {
|
|
185
|
-
if (!
|
|
186
|
-
const raw =
|
|
185
|
+
if (!fs28.existsSync(LOCK_FILE)) return false;
|
|
186
|
+
const raw = fs28.readFileSync(LOCK_FILE, "utf-8").trim();
|
|
187
187
|
const { pid, ts } = JSON.parse(raw);
|
|
188
188
|
if (Date.now() - ts > STALE_MS) return false;
|
|
189
189
|
try {
|
|
@@ -198,13 +198,13 @@ function isCaliberRunning() {
|
|
|
198
198
|
}
|
|
199
199
|
function acquireLock() {
|
|
200
200
|
try {
|
|
201
|
-
|
|
201
|
+
fs28.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
202
202
|
} catch {
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
function releaseLock() {
|
|
206
206
|
try {
|
|
207
|
-
if (
|
|
207
|
+
if (fs28.existsSync(LOCK_FILE)) fs28.unlinkSync(LOCK_FILE);
|
|
208
208
|
} catch {
|
|
209
209
|
}
|
|
210
210
|
}
|
|
@@ -212,14 +212,14 @@ var LOCK_FILE, STALE_MS;
|
|
|
212
212
|
var init_lock = __esm({
|
|
213
213
|
"src/lib/lock.ts"() {
|
|
214
214
|
"use strict";
|
|
215
|
-
LOCK_FILE =
|
|
215
|
+
LOCK_FILE = path22.join(os4.tmpdir(), ".caliber.lock");
|
|
216
216
|
STALE_MS = 10 * 60 * 1e3;
|
|
217
217
|
}
|
|
218
218
|
});
|
|
219
219
|
|
|
220
220
|
// src/cli.ts
|
|
221
221
|
import { Command } from "commander";
|
|
222
|
-
import
|
|
222
|
+
import fs32 from "fs";
|
|
223
223
|
import path25 from "path";
|
|
224
224
|
import { fileURLToPath } from "url";
|
|
225
225
|
|
|
@@ -2030,6 +2030,8 @@ Rules:
|
|
|
2030
2030
|
- Be conservative \u2014 don't rewrite sections that aren't affected by the changes
|
|
2031
2031
|
- Don't add speculative or aspirational content
|
|
2032
2032
|
- Keep managed blocks (<!-- caliber:managed --> ... <!-- /caliber:managed -->) intact
|
|
2033
|
+
- Do NOT modify CALIBER_LEARNINGS.md \u2014 it is managed separately by the learning system
|
|
2034
|
+
- Preserve any references to CALIBER_LEARNINGS.md in CLAUDE.md
|
|
2033
2035
|
- If a doc doesn't need updating, return null for it
|
|
2034
2036
|
- For CLAUDE.md: update commands, architecture notes, conventions, key files if the diffs affect them. Keep under 150 lines.
|
|
2035
2037
|
- For README.md: update setup instructions, API docs, or feature descriptions if affected
|
|
@@ -2061,20 +2063,25 @@ Your job is to reason deeply about these events and identify:
|
|
|
2061
2063
|
3. **Workarounds**: When the agent had to abandon one approach entirely and use a different strategy
|
|
2062
2064
|
4. **Repeated struggles**: The same tool being called many times against the same target, indicating confusion or trial-and-error
|
|
2063
2065
|
5. **Project-specific conventions**: Commands, paths, patterns, or configurations that are specific to this project and would help future sessions
|
|
2066
|
+
6. **Anti-patterns**: Commands, approaches, or configurations that consistently fail or cause problems \u2014 things future sessions should explicitly avoid
|
|
2064
2067
|
|
|
2065
2068
|
From these observations, produce:
|
|
2066
2069
|
|
|
2067
2070
|
### claudeMdLearnedSection
|
|
2068
|
-
A markdown section with concise, actionable bullet points
|
|
2071
|
+
A markdown section with concise, actionable bullet points. Your output will be written to CALIBER_LEARNINGS.md \u2014 a standalone file that all AI coding agents (Claude Code, Cursor, Codex) reference for project-specific patterns and anti-patterns. Each bullet should be a concrete instruction that prevents a past mistake or encodes a discovered convention. Examples:
|
|
2069
2072
|
- "Always run \`npm install\` before \`npm run build\` in this project"
|
|
2070
2073
|
- "The test database requires \`DATABASE_URL\` to be set \u2014 use \`source .env.test\` first"
|
|
2071
2074
|
- "TypeScript strict mode is enabled \u2014 never use \`any\`, use \`unknown\` with type guards"
|
|
2072
2075
|
- "Use \`pnpm\` not \`npm\` \u2014 the lockfile is pnpm-lock.yaml"
|
|
2076
|
+
- "Never use \`npm\` in this project \u2014 pnpm-lock.yaml is the lockfile"
|
|
2077
|
+
- "Do NOT run \`jest\` directly \u2014 always use \`npm run test\` which sets the correct env"
|
|
2078
|
+
- "Avoid modifying files in \`src/generated/\` \u2014 they are auto-generated by the build step"
|
|
2073
2079
|
|
|
2074
2080
|
Rules for the learned section:
|
|
2075
2081
|
- Be additive: keep all existing learned items, add new ones, remove duplicates
|
|
2076
2082
|
- Never repeat instructions already present in the main CLAUDE.md
|
|
2077
2083
|
- Each bullet must be specific and actionable \u2014 no vague advice
|
|
2084
|
+
- Include both positive directives ('Always do X') and negative rules ('Never do Y because Z') when the session evidence supports them
|
|
2078
2085
|
- Maximum ~30 bullet items total
|
|
2079
2086
|
- Group related items under subheadings if there are many
|
|
2080
2087
|
- If there's nothing meaningful to learn, return null
|
|
@@ -3593,7 +3600,8 @@ function getPrecommitBlock() {
|
|
|
3593
3600
|
if [ -x "${bin}" ] || command -v "${bin}" >/dev/null 2>&1; then
|
|
3594
3601
|
echo "\\033[2mcaliber: refreshing docs...\\033[0m"
|
|
3595
3602
|
"${bin}" refresh 2>/dev/null || true
|
|
3596
|
-
|
|
3603
|
+
"${bin}" learn finalize 2>/dev/null || true
|
|
3604
|
+
git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md 2>/dev/null | xargs git add 2>/dev/null || true
|
|
3597
3605
|
fi
|
|
3598
3606
|
${PRECOMMIT_END}`;
|
|
3599
3607
|
}
|
|
@@ -3717,6 +3725,69 @@ function installLearningHooks() {
|
|
|
3717
3725
|
writeSettings2(settings);
|
|
3718
3726
|
return { installed: true, alreadyInstalled: false };
|
|
3719
3727
|
}
|
|
3728
|
+
var CURSOR_HOOKS_PATH = path13.join(".cursor", "hooks.json");
|
|
3729
|
+
var CURSOR_HOOK_EVENTS = [
|
|
3730
|
+
{ event: "postToolUse", tail: "learn observe" },
|
|
3731
|
+
{ event: "postToolUseFailure", tail: "learn observe --failure" },
|
|
3732
|
+
{ event: "sessionEnd", tail: "learn finalize" }
|
|
3733
|
+
];
|
|
3734
|
+
function readCursorHooks() {
|
|
3735
|
+
if (!fs18.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
|
|
3736
|
+
try {
|
|
3737
|
+
return JSON.parse(fs18.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
|
|
3738
|
+
} catch {
|
|
3739
|
+
return { version: 1, hooks: {} };
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
function writeCursorHooks(config) {
|
|
3743
|
+
const dir = path13.dirname(CURSOR_HOOKS_PATH);
|
|
3744
|
+
if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
|
|
3745
|
+
fs18.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
|
|
3746
|
+
}
|
|
3747
|
+
function hasCursorHook(entries, tail) {
|
|
3748
|
+
return entries.some((e) => isCaliberCommand(e.command, tail));
|
|
3749
|
+
}
|
|
3750
|
+
function areCursorLearningHooksInstalled() {
|
|
3751
|
+
const config = readCursorHooks();
|
|
3752
|
+
return CURSOR_HOOK_EVENTS.every((cfg) => {
|
|
3753
|
+
const entries = config.hooks[cfg.event];
|
|
3754
|
+
return Array.isArray(entries) && hasCursorHook(entries, cfg.tail);
|
|
3755
|
+
});
|
|
3756
|
+
}
|
|
3757
|
+
function installCursorLearningHooks() {
|
|
3758
|
+
if (areCursorLearningHooksInstalled()) {
|
|
3759
|
+
return { installed: false, alreadyInstalled: true };
|
|
3760
|
+
}
|
|
3761
|
+
const config = readCursorHooks();
|
|
3762
|
+
const bin = resolveCaliber();
|
|
3763
|
+
for (const cfg of CURSOR_HOOK_EVENTS) {
|
|
3764
|
+
if (!Array.isArray(config.hooks[cfg.event])) {
|
|
3765
|
+
config.hooks[cfg.event] = [];
|
|
3766
|
+
}
|
|
3767
|
+
if (!hasCursorHook(config.hooks[cfg.event], cfg.tail)) {
|
|
3768
|
+
config.hooks[cfg.event].push({ command: `${bin} ${cfg.tail}` });
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
writeCursorHooks(config);
|
|
3772
|
+
return { installed: true, alreadyInstalled: false };
|
|
3773
|
+
}
|
|
3774
|
+
function removeCursorLearningHooks() {
|
|
3775
|
+
const config = readCursorHooks();
|
|
3776
|
+
let removedAny = false;
|
|
3777
|
+
for (const cfg of CURSOR_HOOK_EVENTS) {
|
|
3778
|
+
const entries = config.hooks[cfg.event];
|
|
3779
|
+
if (!Array.isArray(entries)) continue;
|
|
3780
|
+
const idx = entries.findIndex((e) => isCaliberCommand(e.command, cfg.tail));
|
|
3781
|
+
if (idx !== -1) {
|
|
3782
|
+
entries.splice(idx, 1);
|
|
3783
|
+
removedAny = true;
|
|
3784
|
+
if (entries.length === 0) delete config.hooks[cfg.event];
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
if (!removedAny) return { removed: false, notFound: true };
|
|
3788
|
+
writeCursorHooks(config);
|
|
3789
|
+
return { removed: true, notFound: false };
|
|
3790
|
+
}
|
|
3720
3791
|
function removeLearningHooks() {
|
|
3721
3792
|
const settings = readSettings2();
|
|
3722
3793
|
if (!settings.hooks) return { removed: false, notFound: true };
|
|
@@ -3998,6 +4069,7 @@ var POINTS_PERMISSIONS = 2;
|
|
|
3998
4069
|
var POINTS_HOOKS = 2;
|
|
3999
4070
|
var POINTS_AGENTS_MD = 1;
|
|
4000
4071
|
var POINTS_OPEN_SKILLS_FORMAT = 2;
|
|
4072
|
+
var POINTS_LEARNED_CONTENT = 2;
|
|
4001
4073
|
var TOKEN_BUDGET_THRESHOLDS = [
|
|
4002
4074
|
{ maxTokens: 2e3, points: 6 },
|
|
4003
4075
|
{ maxTokens: 3500, points: 5 },
|
|
@@ -5106,6 +5178,18 @@ function checkBonus(dir) {
|
|
|
5106
5178
|
instruction: "Migrate flat skill files to .claude/skills/{name}/SKILL.md with YAML frontmatter."
|
|
5107
5179
|
} : void 0
|
|
5108
5180
|
});
|
|
5181
|
+
const learningsContent = readFileOrNull2(join7(dir, "CALIBER_LEARNINGS.md"));
|
|
5182
|
+
const hasLearned = learningsContent ? learningsContent.split("\n").filter((l) => l.startsWith("- ")).length > 0 : false;
|
|
5183
|
+
checks.push({
|
|
5184
|
+
id: "learned_content",
|
|
5185
|
+
name: "Learned content present",
|
|
5186
|
+
category: "bonus",
|
|
5187
|
+
maxPoints: POINTS_LEARNED_CONTENT,
|
|
5188
|
+
earnedPoints: hasLearned ? POINTS_LEARNED_CONTENT : 0,
|
|
5189
|
+
passed: hasLearned,
|
|
5190
|
+
detail: hasLearned ? "Session learnings found in CALIBER_LEARNINGS.md" : "No learned content",
|
|
5191
|
+
suggestion: hasLearned ? void 0 : "Install learning hooks: `caliber learn install`"
|
|
5192
|
+
});
|
|
5109
5193
|
return checks;
|
|
5110
5194
|
}
|
|
5111
5195
|
|
|
@@ -7314,8 +7398,8 @@ async function scoreCommand(options) {
|
|
|
7314
7398
|
}
|
|
7315
7399
|
|
|
7316
7400
|
// src/commands/refresh.ts
|
|
7317
|
-
import
|
|
7318
|
-
import
|
|
7401
|
+
import fs29 from "fs";
|
|
7402
|
+
import path23 from "path";
|
|
7319
7403
|
import chalk14 from "chalk";
|
|
7320
7404
|
import ora5 from "ora";
|
|
7321
7405
|
|
|
@@ -7327,7 +7411,8 @@ var DOC_PATTERNS = [
|
|
|
7327
7411
|
"README.md",
|
|
7328
7412
|
".cursorrules",
|
|
7329
7413
|
".cursor/rules/",
|
|
7330
|
-
".claude/skills/"
|
|
7414
|
+
".claude/skills/",
|
|
7415
|
+
"CALIBER_LEARNINGS.md"
|
|
7331
7416
|
];
|
|
7332
7417
|
function excludeArgs() {
|
|
7333
7418
|
return DOC_PATTERNS.flatMap((p) => ["--", `:!${p}`]);
|
|
@@ -7431,8 +7516,8 @@ function writeRefreshDocs(docs) {
|
|
|
7431
7516
|
|
|
7432
7517
|
// src/ai/refresh.ts
|
|
7433
7518
|
init_config();
|
|
7434
|
-
async function refreshDocs(diff, existingDocs, projectContext) {
|
|
7435
|
-
const prompt = buildRefreshPrompt(diff, existingDocs, projectContext);
|
|
7519
|
+
async function refreshDocs(diff, existingDocs, projectContext, learnedSection) {
|
|
7520
|
+
const prompt = buildRefreshPrompt(diff, existingDocs, projectContext, learnedSection);
|
|
7436
7521
|
const fastModel = getFastModel();
|
|
7437
7522
|
const raw = await llmCall({
|
|
7438
7523
|
system: REFRESH_SYSTEM_PROMPT,
|
|
@@ -7442,7 +7527,7 @@ async function refreshDocs(diff, existingDocs, projectContext) {
|
|
|
7442
7527
|
});
|
|
7443
7528
|
return parseJsonResponse(raw);
|
|
7444
7529
|
}
|
|
7445
|
-
function buildRefreshPrompt(diff, existingDocs, projectContext) {
|
|
7530
|
+
function buildRefreshPrompt(diff, existingDocs, projectContext, learnedSection) {
|
|
7446
7531
|
const parts = [];
|
|
7447
7532
|
parts.push("Update documentation based on the following code changes.\n");
|
|
7448
7533
|
if (projectContext.packageName) parts.push(`Project: ${projectContext.packageName}`);
|
|
@@ -7490,9 +7575,144 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
7490
7575
|
parts.push(rule.content);
|
|
7491
7576
|
}
|
|
7492
7577
|
}
|
|
7578
|
+
if (learnedSection) {
|
|
7579
|
+
parts.push("\n--- Learned Patterns (from session learning) ---");
|
|
7580
|
+
parts.push("Consider these accumulated learnings when deciding what to update:");
|
|
7581
|
+
parts.push(learnedSection);
|
|
7582
|
+
}
|
|
7493
7583
|
return parts.join("\n");
|
|
7494
7584
|
}
|
|
7495
7585
|
|
|
7586
|
+
// src/learner/writer.ts
|
|
7587
|
+
import fs27 from "fs";
|
|
7588
|
+
import path21 from "path";
|
|
7589
|
+
var LEARNINGS_FILE = "CALIBER_LEARNINGS.md";
|
|
7590
|
+
var LEARNINGS_HEADER = `# Caliber Learnings
|
|
7591
|
+
|
|
7592
|
+
Accumulated patterns and anti-patterns from development sessions.
|
|
7593
|
+
Auto-managed by [caliber](https://github.com/rely-ai-org/caliber) \u2014 do not edit manually.
|
|
7594
|
+
|
|
7595
|
+
`;
|
|
7596
|
+
var LEARNED_START = "<!-- caliber:learned -->";
|
|
7597
|
+
var LEARNED_END = "<!-- /caliber:learned -->";
|
|
7598
|
+
var MAX_LEARNED_ITEMS = 30;
|
|
7599
|
+
function writeLearnedContent(update) {
|
|
7600
|
+
const written = [];
|
|
7601
|
+
let newItemCount = 0;
|
|
7602
|
+
let newItems = [];
|
|
7603
|
+
if (update.claudeMdLearnedSection) {
|
|
7604
|
+
const result = writeLearnedSection(update.claudeMdLearnedSection);
|
|
7605
|
+
newItemCount = result.newCount;
|
|
7606
|
+
newItems = result.newItems;
|
|
7607
|
+
written.push(LEARNINGS_FILE);
|
|
7608
|
+
}
|
|
7609
|
+
if (update.skills?.length) {
|
|
7610
|
+
for (const skill of update.skills) {
|
|
7611
|
+
const skillPath = writeLearnedSkill(skill);
|
|
7612
|
+
written.push(skillPath);
|
|
7613
|
+
}
|
|
7614
|
+
}
|
|
7615
|
+
return { written, newItemCount, newItems };
|
|
7616
|
+
}
|
|
7617
|
+
function parseBullets(content) {
|
|
7618
|
+
const lines = content.split("\n");
|
|
7619
|
+
const bullets = [];
|
|
7620
|
+
let current = "";
|
|
7621
|
+
for (const line of lines) {
|
|
7622
|
+
if (line.startsWith("- ")) {
|
|
7623
|
+
if (current) bullets.push(current);
|
|
7624
|
+
current = line;
|
|
7625
|
+
} else if (current && line.trim() && !line.startsWith("#")) {
|
|
7626
|
+
current += "\n" + line;
|
|
7627
|
+
} else {
|
|
7628
|
+
if (current) bullets.push(current);
|
|
7629
|
+
current = "";
|
|
7630
|
+
}
|
|
7631
|
+
}
|
|
7632
|
+
if (current) bullets.push(current);
|
|
7633
|
+
return bullets;
|
|
7634
|
+
}
|
|
7635
|
+
function normalizeBullet(bullet) {
|
|
7636
|
+
return bullet.replace(/^- /, "").replace(/`[^`]*`/g, "").replace(/\s+/g, " ").toLowerCase().trim();
|
|
7637
|
+
}
|
|
7638
|
+
function deduplicateLearnedItems(existing, incoming) {
|
|
7639
|
+
const existingBullets = existing ? parseBullets(existing) : [];
|
|
7640
|
+
const incomingBullets = parseBullets(incoming);
|
|
7641
|
+
const merged = [...existingBullets];
|
|
7642
|
+
const newItems = [];
|
|
7643
|
+
for (const bullet of incomingBullets) {
|
|
7644
|
+
const norm = normalizeBullet(bullet);
|
|
7645
|
+
if (!norm) continue;
|
|
7646
|
+
const isDup = merged.some((e) => {
|
|
7647
|
+
const eNorm = normalizeBullet(e);
|
|
7648
|
+
const shorter = Math.min(norm.length, eNorm.length);
|
|
7649
|
+
const longer = Math.max(norm.length, eNorm.length);
|
|
7650
|
+
if (!(eNorm.includes(norm) || norm.includes(eNorm))) return false;
|
|
7651
|
+
return shorter / longer > 0.7;
|
|
7652
|
+
});
|
|
7653
|
+
if (!isDup) {
|
|
7654
|
+
merged.push(bullet);
|
|
7655
|
+
newItems.push(bullet);
|
|
7656
|
+
}
|
|
7657
|
+
}
|
|
7658
|
+
const capped = merged.length > MAX_LEARNED_ITEMS ? merged.slice(-MAX_LEARNED_ITEMS) : merged;
|
|
7659
|
+
return { merged: capped.join("\n"), newCount: newItems.length, newItems };
|
|
7660
|
+
}
|
|
7661
|
+
function writeLearnedSection(content) {
|
|
7662
|
+
const existingSection = readLearnedSection();
|
|
7663
|
+
const { merged, newCount, newItems } = deduplicateLearnedItems(existingSection, content);
|
|
7664
|
+
fs27.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + merged + "\n");
|
|
7665
|
+
return { newCount, newItems };
|
|
7666
|
+
}
|
|
7667
|
+
function writeLearnedSkill(skill) {
|
|
7668
|
+
const skillDir = path21.join(".claude", "skills", skill.name);
|
|
7669
|
+
if (!fs27.existsSync(skillDir)) fs27.mkdirSync(skillDir, { recursive: true });
|
|
7670
|
+
const skillPath = path21.join(skillDir, "SKILL.md");
|
|
7671
|
+
if (!skill.isNew && fs27.existsSync(skillPath)) {
|
|
7672
|
+
const existing = fs27.readFileSync(skillPath, "utf-8");
|
|
7673
|
+
fs27.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
7674
|
+
} else {
|
|
7675
|
+
const frontmatter = [
|
|
7676
|
+
"---",
|
|
7677
|
+
`name: ${skill.name}`,
|
|
7678
|
+
`description: ${skill.description}`,
|
|
7679
|
+
"---",
|
|
7680
|
+
""
|
|
7681
|
+
].join("\n");
|
|
7682
|
+
fs27.writeFileSync(skillPath, frontmatter + skill.content);
|
|
7683
|
+
}
|
|
7684
|
+
return skillPath;
|
|
7685
|
+
}
|
|
7686
|
+
function readLearnedSection() {
|
|
7687
|
+
if (fs27.existsSync(LEARNINGS_FILE)) {
|
|
7688
|
+
const content2 = fs27.readFileSync(LEARNINGS_FILE, "utf-8");
|
|
7689
|
+
const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
|
|
7690
|
+
return bullets || null;
|
|
7691
|
+
}
|
|
7692
|
+
const claudeMdPath = "CLAUDE.md";
|
|
7693
|
+
if (!fs27.existsSync(claudeMdPath)) return null;
|
|
7694
|
+
const content = fs27.readFileSync(claudeMdPath, "utf-8");
|
|
7695
|
+
const startIdx = content.indexOf(LEARNED_START);
|
|
7696
|
+
const endIdx = content.indexOf(LEARNED_END);
|
|
7697
|
+
if (startIdx === -1 || endIdx === -1) return null;
|
|
7698
|
+
return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
|
|
7699
|
+
}
|
|
7700
|
+
function migrateInlineLearnings() {
|
|
7701
|
+
if (fs27.existsSync(LEARNINGS_FILE)) return false;
|
|
7702
|
+
const claudeMdPath = "CLAUDE.md";
|
|
7703
|
+
if (!fs27.existsSync(claudeMdPath)) return false;
|
|
7704
|
+
const content = fs27.readFileSync(claudeMdPath, "utf-8");
|
|
7705
|
+
const startIdx = content.indexOf(LEARNED_START);
|
|
7706
|
+
const endIdx = content.indexOf(LEARNED_END);
|
|
7707
|
+
if (startIdx === -1 || endIdx === -1) return false;
|
|
7708
|
+
const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
|
|
7709
|
+
if (!section) return false;
|
|
7710
|
+
fs27.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
|
|
7711
|
+
const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
|
|
7712
|
+
fs27.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
|
|
7713
|
+
return true;
|
|
7714
|
+
}
|
|
7715
|
+
|
|
7496
7716
|
// src/commands/refresh.ts
|
|
7497
7717
|
init_config();
|
|
7498
7718
|
function log2(quiet, ...args) {
|
|
@@ -7501,11 +7721,11 @@ function log2(quiet, ...args) {
|
|
|
7501
7721
|
function discoverGitRepos(parentDir) {
|
|
7502
7722
|
const repos = [];
|
|
7503
7723
|
try {
|
|
7504
|
-
const entries =
|
|
7724
|
+
const entries = fs29.readdirSync(parentDir, { withFileTypes: true });
|
|
7505
7725
|
for (const entry of entries) {
|
|
7506
7726
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
7507
|
-
const childPath =
|
|
7508
|
-
if (
|
|
7727
|
+
const childPath = path23.join(parentDir, entry.name);
|
|
7728
|
+
if (fs29.existsSync(path23.join(childPath, ".git"))) {
|
|
7509
7729
|
repos.push(childPath);
|
|
7510
7730
|
}
|
|
7511
7731
|
}
|
|
@@ -7529,6 +7749,7 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
7529
7749
|
}
|
|
7530
7750
|
const spinner = quiet ? null : ora5(`${prefix}Analyzing changes...`).start();
|
|
7531
7751
|
const existingDocs = readExistingConfigs(repoDir);
|
|
7752
|
+
const learnedSection = readLearnedSection();
|
|
7532
7753
|
const fingerprint = await collectFingerprint(repoDir);
|
|
7533
7754
|
const projectContext = {
|
|
7534
7755
|
languages: fingerprint.languages,
|
|
@@ -7544,7 +7765,8 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
7544
7765
|
summary: diff.summary
|
|
7545
7766
|
},
|
|
7546
7767
|
existingDocs,
|
|
7547
|
-
projectContext
|
|
7768
|
+
projectContext,
|
|
7769
|
+
learnedSection
|
|
7548
7770
|
);
|
|
7549
7771
|
if (!response.docsUpdated || response.docsUpdated.length === 0) {
|
|
7550
7772
|
spinner?.succeed(`${prefix}No doc updates needed`);
|
|
@@ -7606,7 +7828,7 @@ async function refreshCommand(options) {
|
|
|
7606
7828
|
`));
|
|
7607
7829
|
const originalDir = process.cwd();
|
|
7608
7830
|
for (const repo of repos) {
|
|
7609
|
-
const repoName =
|
|
7831
|
+
const repoName = path23.basename(repo);
|
|
7610
7832
|
try {
|
|
7611
7833
|
process.chdir(repo);
|
|
7612
7834
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
@@ -7830,6 +8052,7 @@ async function configCommand() {
|
|
|
7830
8052
|
}
|
|
7831
8053
|
|
|
7832
8054
|
// src/commands/learn.ts
|
|
8055
|
+
import fs31 from "fs";
|
|
7833
8056
|
import chalk17 from "chalk";
|
|
7834
8057
|
|
|
7835
8058
|
// src/learner/stdin.ts
|
|
@@ -7861,8 +8084,8 @@ function readStdin() {
|
|
|
7861
8084
|
|
|
7862
8085
|
// src/learner/storage.ts
|
|
7863
8086
|
init_constants();
|
|
7864
|
-
import
|
|
7865
|
-
import
|
|
8087
|
+
import fs30 from "fs";
|
|
8088
|
+
import path24 from "path";
|
|
7866
8089
|
var MAX_RESPONSE_LENGTH = 2e3;
|
|
7867
8090
|
var DEFAULT_STATE = {
|
|
7868
8091
|
sessionId: null,
|
|
@@ -7870,15 +8093,15 @@ var DEFAULT_STATE = {
|
|
|
7870
8093
|
lastAnalysisTimestamp: null
|
|
7871
8094
|
};
|
|
7872
8095
|
function ensureLearningDir() {
|
|
7873
|
-
if (!
|
|
7874
|
-
|
|
8096
|
+
if (!fs30.existsSync(LEARNING_DIR)) {
|
|
8097
|
+
fs30.mkdirSync(LEARNING_DIR, { recursive: true });
|
|
7875
8098
|
}
|
|
7876
8099
|
}
|
|
7877
8100
|
function sessionFilePath() {
|
|
7878
|
-
return
|
|
8101
|
+
return path24.join(LEARNING_DIR, LEARNING_SESSION_FILE);
|
|
7879
8102
|
}
|
|
7880
8103
|
function stateFilePath() {
|
|
7881
|
-
return
|
|
8104
|
+
return path24.join(LEARNING_DIR, LEARNING_STATE_FILE);
|
|
7882
8105
|
}
|
|
7883
8106
|
function truncateResponse(response) {
|
|
7884
8107
|
const str = JSON.stringify(response);
|
|
@@ -7889,113 +8112,84 @@ function appendEvent(event) {
|
|
|
7889
8112
|
ensureLearningDir();
|
|
7890
8113
|
const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
|
|
7891
8114
|
const filePath = sessionFilePath();
|
|
7892
|
-
|
|
8115
|
+
fs30.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
7893
8116
|
const count = getEventCount();
|
|
7894
8117
|
if (count > LEARNING_MAX_EVENTS) {
|
|
7895
|
-
const lines =
|
|
8118
|
+
const lines = fs30.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
7896
8119
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
7897
|
-
|
|
8120
|
+
fs30.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
7898
8121
|
}
|
|
7899
8122
|
}
|
|
7900
8123
|
function readAllEvents() {
|
|
7901
8124
|
const filePath = sessionFilePath();
|
|
7902
|
-
if (!
|
|
7903
|
-
const lines =
|
|
8125
|
+
if (!fs30.existsSync(filePath)) return [];
|
|
8126
|
+
const lines = fs30.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
7904
8127
|
return lines.map((line) => JSON.parse(line));
|
|
7905
8128
|
}
|
|
7906
8129
|
function getEventCount() {
|
|
7907
8130
|
const filePath = sessionFilePath();
|
|
7908
|
-
if (!
|
|
7909
|
-
const content =
|
|
8131
|
+
if (!fs30.existsSync(filePath)) return 0;
|
|
8132
|
+
const content = fs30.readFileSync(filePath, "utf-8");
|
|
7910
8133
|
return content.split("\n").filter(Boolean).length;
|
|
7911
8134
|
}
|
|
7912
8135
|
function clearSession() {
|
|
7913
8136
|
const filePath = sessionFilePath();
|
|
7914
|
-
if (
|
|
8137
|
+
if (fs30.existsSync(filePath)) fs30.unlinkSync(filePath);
|
|
7915
8138
|
}
|
|
7916
8139
|
function readState2() {
|
|
7917
8140
|
const filePath = stateFilePath();
|
|
7918
|
-
if (!
|
|
8141
|
+
if (!fs30.existsSync(filePath)) return { ...DEFAULT_STATE };
|
|
7919
8142
|
try {
|
|
7920
|
-
return JSON.parse(
|
|
8143
|
+
return JSON.parse(fs30.readFileSync(filePath, "utf-8"));
|
|
7921
8144
|
} catch {
|
|
7922
8145
|
return { ...DEFAULT_STATE };
|
|
7923
8146
|
}
|
|
7924
8147
|
}
|
|
7925
8148
|
function writeState2(state) {
|
|
7926
8149
|
ensureLearningDir();
|
|
7927
|
-
|
|
8150
|
+
fs30.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
|
|
7928
8151
|
}
|
|
7929
8152
|
function resetState() {
|
|
7930
8153
|
writeState2({ ...DEFAULT_STATE });
|
|
7931
8154
|
}
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
const
|
|
7940
|
-
if (
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
written.push(skillPath);
|
|
8155
|
+
var LOCK_FILE2 = "finalize.lock";
|
|
8156
|
+
var LOCK_STALE_MS = 5 * 60 * 1e3;
|
|
8157
|
+
function lockFilePath() {
|
|
8158
|
+
return path24.join(LEARNING_DIR, LOCK_FILE2);
|
|
8159
|
+
}
|
|
8160
|
+
function acquireFinalizeLock() {
|
|
8161
|
+
ensureLearningDir();
|
|
8162
|
+
const lockPath = lockFilePath();
|
|
8163
|
+
if (fs30.existsSync(lockPath)) {
|
|
8164
|
+
try {
|
|
8165
|
+
const stat = fs30.statSync(lockPath);
|
|
8166
|
+
if (Date.now() - stat.mtimeMs < LOCK_STALE_MS) {
|
|
8167
|
+
return false;
|
|
8168
|
+
}
|
|
8169
|
+
} catch {
|
|
7948
8170
|
}
|
|
7949
8171
|
}
|
|
7950
|
-
|
|
7951
|
-
}
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
let updated;
|
|
7964
|
-
if (startIdx !== -1 && endIdx !== -1) {
|
|
7965
|
-
updated = existing.slice(0, startIdx) + section + existing.slice(endIdx + LEARNED_END.length);
|
|
7966
|
-
} else {
|
|
7967
|
-
const separator = existing.endsWith("\n") || existing === "" ? "" : "\n";
|
|
7968
|
-
updated = existing + separator + "\n" + section + "\n";
|
|
8172
|
+
try {
|
|
8173
|
+
fs30.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
8174
|
+
return true;
|
|
8175
|
+
} catch {
|
|
8176
|
+
try {
|
|
8177
|
+
const stat = fs30.statSync(lockPath);
|
|
8178
|
+
if (Date.now() - stat.mtimeMs >= LOCK_STALE_MS) {
|
|
8179
|
+
fs30.writeFileSync(lockPath, String(process.pid));
|
|
8180
|
+
return true;
|
|
8181
|
+
}
|
|
8182
|
+
} catch {
|
|
8183
|
+
}
|
|
8184
|
+
return false;
|
|
7969
8185
|
}
|
|
7970
|
-
fs30.writeFileSync(claudeMdPath, updated);
|
|
7971
8186
|
}
|
|
7972
|
-
function
|
|
7973
|
-
const
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
|
|
7977
|
-
const existing = fs30.readFileSync(skillPath, "utf-8");
|
|
7978
|
-
fs30.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
7979
|
-
} else {
|
|
7980
|
-
const frontmatter = [
|
|
7981
|
-
"---",
|
|
7982
|
-
`name: ${skill.name}`,
|
|
7983
|
-
`description: ${skill.description}`,
|
|
7984
|
-
"---",
|
|
7985
|
-
""
|
|
7986
|
-
].join("\n");
|
|
7987
|
-
fs30.writeFileSync(skillPath, frontmatter + skill.content);
|
|
8187
|
+
function releaseFinalizeLock() {
|
|
8188
|
+
const lockPath = lockFilePath();
|
|
8189
|
+
try {
|
|
8190
|
+
if (fs30.existsSync(lockPath)) fs30.unlinkSync(lockPath);
|
|
8191
|
+
} catch {
|
|
7988
8192
|
}
|
|
7989
|
-
return skillPath;
|
|
7990
|
-
}
|
|
7991
|
-
function readLearnedSection() {
|
|
7992
|
-
const claudeMdPath = "CLAUDE.md";
|
|
7993
|
-
if (!fs30.existsSync(claudeMdPath)) return null;
|
|
7994
|
-
const content = fs30.readFileSync(claudeMdPath, "utf-8");
|
|
7995
|
-
const startIdx = content.indexOf(LEARNED_START);
|
|
7996
|
-
const endIdx = content.indexOf(LEARNED_END);
|
|
7997
|
-
if (startIdx === -1 || endIdx === -1) return null;
|
|
7998
|
-
return content.slice(startIdx + LEARNED_START.length, endIdx).trim();
|
|
7999
8193
|
}
|
|
8000
8194
|
|
|
8001
8195
|
// src/ai/learn.ts
|
|
@@ -8074,6 +8268,7 @@ ${eventsText}`;
|
|
|
8074
8268
|
|
|
8075
8269
|
// src/commands/learn.ts
|
|
8076
8270
|
init_config();
|
|
8271
|
+
var MIN_EVENTS_FOR_ANALYSIS = 50;
|
|
8077
8272
|
async function learnObserveCommand(options) {
|
|
8078
8273
|
try {
|
|
8079
8274
|
const raw = await readStdin();
|
|
@@ -8081,11 +8276,11 @@ async function learnObserveCommand(options) {
|
|
|
8081
8276
|
const hookData = JSON.parse(raw);
|
|
8082
8277
|
const event = {
|
|
8083
8278
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8084
|
-
session_id: hookData.session_id || "unknown",
|
|
8279
|
+
session_id: hookData.session_id || hookData.conversation_id || "unknown",
|
|
8085
8280
|
hook_event_name: options.failure ? "PostToolUseFailure" : "PostToolUse",
|
|
8086
8281
|
tool_name: hookData.tool_name || "unknown",
|
|
8087
8282
|
tool_input: hookData.tool_input || {},
|
|
8088
|
-
tool_response: hookData.tool_response || {},
|
|
8283
|
+
tool_response: hookData.tool_response || hookData.tool_output || {},
|
|
8089
8284
|
tool_use_id: hookData.tool_use_id || "",
|
|
8090
8285
|
cwd: hookData.cwd || process.cwd()
|
|
8091
8286
|
};
|
|
@@ -8100,6 +8295,8 @@ async function learnObserveCommand(options) {
|
|
|
8100
8295
|
async function learnFinalizeCommand() {
|
|
8101
8296
|
const { isCaliberRunning: isCaliberRunning2 } = await Promise.resolve().then(() => (init_lock(), lock_exports));
|
|
8102
8297
|
if (isCaliberRunning2()) return;
|
|
8298
|
+
if (!acquireFinalizeLock()) return;
|
|
8299
|
+
let analyzed = false;
|
|
8103
8300
|
try {
|
|
8104
8301
|
const config = loadConfig();
|
|
8105
8302
|
if (!config) {
|
|
@@ -8108,12 +8305,9 @@ async function learnFinalizeCommand() {
|
|
|
8108
8305
|
return;
|
|
8109
8306
|
}
|
|
8110
8307
|
const events = readAllEvents();
|
|
8111
|
-
if (
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
return;
|
|
8115
|
-
}
|
|
8116
|
-
await validateModel();
|
|
8308
|
+
if (events.length < MIN_EVENTS_FOR_ANALYSIS) return;
|
|
8309
|
+
await validateModel({ fast: true });
|
|
8310
|
+
migrateInlineLearnings();
|
|
8117
8311
|
const existingConfigs = readExistingConfigs(process.cwd());
|
|
8118
8312
|
const existingLearnedSection = readLearnedSection();
|
|
8119
8313
|
const existingSkills = existingConfigs.claudeSkills || [];
|
|
@@ -8123,51 +8317,97 @@ async function learnFinalizeCommand() {
|
|
|
8123
8317
|
existingLearnedSection,
|
|
8124
8318
|
existingSkills
|
|
8125
8319
|
);
|
|
8320
|
+
analyzed = true;
|
|
8126
8321
|
if (response.claudeMdLearnedSection || response.skills?.length) {
|
|
8127
|
-
writeLearnedContent({
|
|
8322
|
+
const result = writeLearnedContent({
|
|
8128
8323
|
claudeMdLearnedSection: response.claudeMdLearnedSection,
|
|
8129
8324
|
skills: response.skills
|
|
8130
8325
|
});
|
|
8326
|
+
if (result.newItemCount > 0) {
|
|
8327
|
+
console.log(chalk17.dim(`caliber: learned ${result.newItemCount} new pattern${result.newItemCount === 1 ? "" : "s"}`));
|
|
8328
|
+
for (const item of result.newItems) {
|
|
8329
|
+
console.log(chalk17.dim(` + ${item.replace(/^- /, "").slice(0, 80)}`));
|
|
8330
|
+
}
|
|
8331
|
+
}
|
|
8131
8332
|
}
|
|
8132
8333
|
} catch {
|
|
8133
8334
|
} finally {
|
|
8134
|
-
|
|
8135
|
-
|
|
8335
|
+
if (analyzed) {
|
|
8336
|
+
clearSession();
|
|
8337
|
+
resetState();
|
|
8338
|
+
}
|
|
8339
|
+
releaseFinalizeLock();
|
|
8136
8340
|
}
|
|
8137
8341
|
}
|
|
8138
8342
|
async function learnInstallCommand() {
|
|
8139
|
-
|
|
8140
|
-
if (
|
|
8141
|
-
|
|
8343
|
+
let anyInstalled = false;
|
|
8344
|
+
if (fs31.existsSync(".claude")) {
|
|
8345
|
+
const r = installLearningHooks();
|
|
8346
|
+
if (r.installed) {
|
|
8347
|
+
console.log(chalk17.green("\u2713") + " Claude Code learning hooks installed");
|
|
8348
|
+
anyInstalled = true;
|
|
8349
|
+
} else if (r.alreadyInstalled) {
|
|
8350
|
+
console.log(chalk17.dim(" Claude Code hooks already installed"));
|
|
8351
|
+
}
|
|
8352
|
+
}
|
|
8353
|
+
if (fs31.existsSync(".cursor")) {
|
|
8354
|
+
const r = installCursorLearningHooks();
|
|
8355
|
+
if (r.installed) {
|
|
8356
|
+
console.log(chalk17.green("\u2713") + " Cursor learning hooks installed");
|
|
8357
|
+
anyInstalled = true;
|
|
8358
|
+
} else if (r.alreadyInstalled) {
|
|
8359
|
+
console.log(chalk17.dim(" Cursor hooks already installed"));
|
|
8360
|
+
}
|
|
8361
|
+
}
|
|
8362
|
+
if (!fs31.existsSync(".claude") && !fs31.existsSync(".cursor")) {
|
|
8363
|
+
console.log(chalk17.yellow("No .claude/ or .cursor/ directory found."));
|
|
8364
|
+
console.log(chalk17.dim(" Run `caliber init` first, or create the directory manually."));
|
|
8142
8365
|
return;
|
|
8143
8366
|
}
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8367
|
+
if (anyInstalled) {
|
|
8368
|
+
console.log(chalk17.dim(` Tool usage will be recorded and learnings extracted after \u2265${MIN_EVENTS_FOR_ANALYSIS} events.`));
|
|
8369
|
+
console.log(chalk17.dim(" Learnings written to CALIBER_LEARNINGS.md."));
|
|
8370
|
+
}
|
|
8147
8371
|
}
|
|
8148
8372
|
async function learnRemoveCommand() {
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
|
|
8373
|
+
let anyRemoved = false;
|
|
8374
|
+
const r1 = removeLearningHooks();
|
|
8375
|
+
if (r1.removed) {
|
|
8376
|
+
console.log(chalk17.green("\u2713") + " Claude Code learning hooks removed");
|
|
8377
|
+
anyRemoved = true;
|
|
8378
|
+
}
|
|
8379
|
+
const r2 = removeCursorLearningHooks();
|
|
8380
|
+
if (r2.removed) {
|
|
8381
|
+
console.log(chalk17.green("\u2713") + " Cursor learning hooks removed");
|
|
8382
|
+
anyRemoved = true;
|
|
8383
|
+
}
|
|
8384
|
+
if (!anyRemoved) {
|
|
8385
|
+
console.log(chalk17.dim("No learning hooks found."));
|
|
8153
8386
|
}
|
|
8154
|
-
console.log(chalk17.green("\u2713") + " Learning hooks removed from .claude/settings.json");
|
|
8155
8387
|
}
|
|
8156
8388
|
async function learnStatusCommand() {
|
|
8157
|
-
const
|
|
8389
|
+
const claudeInstalled = areLearningHooksInstalled();
|
|
8390
|
+
const cursorInstalled = areCursorLearningHooksInstalled();
|
|
8158
8391
|
const state = readState2();
|
|
8159
8392
|
const eventCount = getEventCount();
|
|
8160
8393
|
console.log(chalk17.bold("Session Learning Status"));
|
|
8161
8394
|
console.log();
|
|
8162
|
-
if (
|
|
8163
|
-
console.log(chalk17.green("\u2713") + "
|
|
8395
|
+
if (claudeInstalled) {
|
|
8396
|
+
console.log(chalk17.green("\u2713") + " Claude Code hooks " + chalk17.green("installed"));
|
|
8397
|
+
} else {
|
|
8398
|
+
console.log(chalk17.dim("\u2717") + " Claude Code hooks " + chalk17.dim("not installed"));
|
|
8399
|
+
}
|
|
8400
|
+
if (cursorInstalled) {
|
|
8401
|
+
console.log(chalk17.green("\u2713") + " Cursor hooks " + chalk17.green("installed"));
|
|
8164
8402
|
} else {
|
|
8165
|
-
console.log(chalk17.dim("\u2717") + "
|
|
8403
|
+
console.log(chalk17.dim("\u2717") + " Cursor hooks " + chalk17.dim("not installed"));
|
|
8404
|
+
}
|
|
8405
|
+
if (!claudeInstalled && !cursorInstalled) {
|
|
8166
8406
|
console.log(chalk17.dim(" Run `caliber learn install` to enable session learning."));
|
|
8167
8407
|
}
|
|
8168
8408
|
console.log();
|
|
8169
8409
|
console.log(`Events recorded: ${chalk17.cyan(String(eventCount))}`);
|
|
8170
|
-
console.log(`
|
|
8410
|
+
console.log(`Threshold for analysis: ${chalk17.cyan(String(MIN_EVENTS_FOR_ANALYSIS))}`);
|
|
8171
8411
|
if (state.lastAnalysisTimestamp) {
|
|
8172
8412
|
console.log(`Last analysis: ${chalk17.cyan(state.lastAnalysisTimestamp)}`);
|
|
8173
8413
|
} else {
|
|
@@ -8177,14 +8417,14 @@ async function learnStatusCommand() {
|
|
|
8177
8417
|
if (learnedSection) {
|
|
8178
8418
|
const lineCount = learnedSection.split("\n").filter(Boolean).length;
|
|
8179
8419
|
console.log(`
|
|
8180
|
-
Learned items in
|
|
8420
|
+
Learned items in CALIBER_LEARNINGS.md: ${chalk17.cyan(String(lineCount))}`);
|
|
8181
8421
|
}
|
|
8182
8422
|
}
|
|
8183
8423
|
|
|
8184
8424
|
// src/cli.ts
|
|
8185
8425
|
var __dirname = path25.dirname(fileURLToPath(import.meta.url));
|
|
8186
8426
|
var pkg = JSON.parse(
|
|
8187
|
-
|
|
8427
|
+
fs32.readFileSync(path25.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
8188
8428
|
);
|
|
8189
8429
|
var program = new Command();
|
|
8190
8430
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
@@ -8258,7 +8498,7 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
|
|
|
8258
8498
|
learn.command("status").description("Show learning system status").action(tracked("learn:status", learnStatusCommand));
|
|
8259
8499
|
|
|
8260
8500
|
// src/utils/version-check.ts
|
|
8261
|
-
import
|
|
8501
|
+
import fs33 from "fs";
|
|
8262
8502
|
import path26 from "path";
|
|
8263
8503
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8264
8504
|
import { execSync as execSync14 } from "child_process";
|
|
@@ -8267,13 +8507,13 @@ import ora6 from "ora";
|
|
|
8267
8507
|
import confirm2 from "@inquirer/confirm";
|
|
8268
8508
|
var __dirname_vc = path26.dirname(fileURLToPath2(import.meta.url));
|
|
8269
8509
|
var pkg2 = JSON.parse(
|
|
8270
|
-
|
|
8510
|
+
fs33.readFileSync(path26.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
8271
8511
|
);
|
|
8272
8512
|
function getInstalledVersion() {
|
|
8273
8513
|
try {
|
|
8274
8514
|
const globalRoot = execSync14("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
8275
8515
|
const pkgPath = path26.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
8276
|
-
return JSON.parse(
|
|
8516
|
+
return JSON.parse(fs33.readFileSync(pkgPath, "utf-8")).version;
|
|
8277
8517
|
} catch {
|
|
8278
8518
|
return null;
|
|
8279
8519
|
}
|
package/package.json
CHANGED