@rely-ai/caliber 1.19.7 → 1.20.0-dev.1773685589
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 +114 -28
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2062,7 +2062,7 @@ Return a JSON object with this exact shape:
|
|
|
2062
2062
|
Respond with ONLY the JSON object, no markdown fences or extra text.`;
|
|
2063
2063
|
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.
|
|
2064
2064
|
|
|
2065
|
-
You receive a chronological sequence of
|
|
2065
|
+
You receive a chronological sequence of events from a Claude Code session. Most events are tool calls (with tool name, input, response, and success/failure status). Some events are USER_PROMPT events that capture what the user typed \u2014 these are critical for detecting corrections and redirections.
|
|
2066
2066
|
|
|
2067
2067
|
Your job is to find OPERATIONAL patterns \u2014 things that went wrong and how they were fixed, commands that required specific flags or configuration, APIs that needed a particular approach to work. Focus on the WORKFLOW, not the code logic.
|
|
2068
2068
|
|
|
@@ -2074,6 +2074,7 @@ Look for:
|
|
|
2074
2074
|
4. **Project-specific commands**: The correct way to build, test, lint, deploy \u2014 especially if it differs from defaults.
|
|
2075
2075
|
5. **File/path traps**: Paths that are misleading, files that shouldn't be edited, directories with unexpected structure.
|
|
2076
2076
|
6. **Configuration quirks**: Settings, flags, or arguments that are required but non-obvious.
|
|
2077
|
+
7. **User corrections**: The user explicitly told the AI what's wrong, what to use instead, or what to avoid. Look for phrases like "no, use X instead of Y", "don't touch/edit/modify X", "that's wrong, you need to...", "always/never do X in this project", "stop, that file is...". These are the HIGHEST VALUE signals \u2014 they represent direct human feedback about project-specific requirements. If a user correction contradicts a pattern you'd otherwise extract, the correction wins.
|
|
2077
2078
|
|
|
2078
2079
|
DO NOT extract:
|
|
2079
2080
|
- Descriptions of what the code does or how features work (e.g. "compression removes comments" or "skeleton extraction creates outlines")
|
|
@@ -2084,21 +2085,30 @@ DO NOT extract:
|
|
|
2084
2085
|
From these observations, produce:
|
|
2085
2086
|
|
|
2086
2087
|
### claudeMdLearnedSection
|
|
2087
|
-
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 operational patterns.
|
|
2088
|
+
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 operational patterns.
|
|
2089
|
+
|
|
2090
|
+
Each bullet MUST be prefixed with an observation type in bold brackets. Valid types:
|
|
2091
|
+
- **[correction]** \u2014 user explicitly told the AI what's wrong or what to do differently (HIGHEST PRIORITY \u2014 always include these)
|
|
2092
|
+
- **[gotcha]** \u2014 a trap or edge case that wastes time if you don't know about it
|
|
2093
|
+
- **[fix]** \u2014 a specific failure-to-recovery sequence
|
|
2094
|
+
- **[pattern]** \u2014 a reusable approach that works in this project
|
|
2095
|
+
- **[env]** \u2014 an environment or configuration requirement
|
|
2096
|
+
- **[convention]** \u2014 a project-specific rule or naming convention
|
|
2088
2097
|
|
|
2089
2098
|
Good examples:
|
|
2090
|
-
- "
|
|
2091
|
-
- "
|
|
2092
|
-
- "
|
|
2093
|
-
- "
|
|
2094
|
-
- "
|
|
2095
|
-
- "
|
|
2096
|
-
- "
|
|
2099
|
+
- "**[correction]** Files in \`src/generated/\` are auto-generated \u2014 never edit them directly"
|
|
2100
|
+
- "**[correction]** Use \`pnpm\` not \`npm\` \u2014 the lockfile is pnpm-lock.yaml and npm creates conflicts"
|
|
2101
|
+
- "**[gotcha]** When \`tsup\` build fails with a type error, run \`npx tsc --noEmit\` first to get the real error \u2014 tsup swallows the details"
|
|
2102
|
+
- "**[fix]** If \`npm install\` fails with ERESOLVE, use \`--legacy-peer-deps\`"
|
|
2103
|
+
- "**[env]** The test database requires \`DATABASE_URL\` to be set \u2014 use \`source .env.test\` first"
|
|
2104
|
+
- "**[pattern]** Do NOT run \`jest\` directly \u2014 always use \`npm run test\` which sets the correct NODE_ENV"
|
|
2105
|
+
- "**[convention]** API calls to \`/v2/users\` require the \`X-Api-Version\` header \u2014 without it you get a 404 that looks like the endpoint doesn't exist"
|
|
2097
2106
|
|
|
2098
2107
|
Bad examples (do NOT produce these):
|
|
2099
2108
|
- "The codebase uses TypeScript with strict mode" (describes code, not actionable)
|
|
2100
2109
|
- "Components follow a pattern of X" (describes architecture, not operational)
|
|
2101
2110
|
- "The project has a scoring module" (summarizes code structure)
|
|
2111
|
+
- Any bullet without a **[type]** prefix
|
|
2102
2112
|
|
|
2103
2113
|
Rules for the learned section:
|
|
2104
2114
|
- Be additive: keep all existing learned items, add new ones, remove duplicates
|
|
@@ -3686,6 +3696,7 @@ var SETTINGS_PATH2 = path13.join(".claude", "settings.json");
|
|
|
3686
3696
|
var HOOK_TAILS = [
|
|
3687
3697
|
{ event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
|
|
3688
3698
|
{ event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
|
|
3699
|
+
{ event: "UserPromptSubmit", tail: "learn observe --prompt", description: "Caliber: recording user prompt for correction detection" },
|
|
3689
3700
|
{ event: "SessionEnd", tail: "learn finalize", description: "Caliber: finalizing session learnings" }
|
|
3690
3701
|
];
|
|
3691
3702
|
function getHookConfigs() {
|
|
@@ -3746,6 +3757,7 @@ var CURSOR_HOOKS_PATH = path13.join(".cursor", "hooks.json");
|
|
|
3746
3757
|
var CURSOR_HOOK_EVENTS = [
|
|
3747
3758
|
{ event: "postToolUse", tail: "learn observe" },
|
|
3748
3759
|
{ event: "postToolUseFailure", tail: "learn observe --failure" },
|
|
3760
|
+
{ event: "userPromptSubmit", tail: "learn observe --prompt" },
|
|
3749
3761
|
{ event: "sessionEnd", tail: "learn finalize" }
|
|
3750
3762
|
];
|
|
3751
3763
|
function readCursorHooks() {
|
|
@@ -7649,8 +7661,12 @@ function parseBullets(content) {
|
|
|
7649
7661
|
if (current) bullets.push(current);
|
|
7650
7662
|
return bullets;
|
|
7651
7663
|
}
|
|
7664
|
+
var TYPE_PREFIX_RE = /^\*\*\[[^\]]+\]\*\*\s*/;
|
|
7652
7665
|
function normalizeBullet(bullet) {
|
|
7653
|
-
return bullet.replace(/^- /, "").replace(/`[^`]*`/g, "").replace(/\s+/g, " ").toLowerCase().trim();
|
|
7666
|
+
return bullet.replace(/^- /, "").replace(TYPE_PREFIX_RE, "").replace(/`[^`]*`/g, "").replace(/\s+/g, " ").toLowerCase().trim();
|
|
7667
|
+
}
|
|
7668
|
+
function hasTypePrefix(bullet) {
|
|
7669
|
+
return TYPE_PREFIX_RE.test(bullet.replace(/^- /, ""));
|
|
7654
7670
|
}
|
|
7655
7671
|
function deduplicateLearnedItems(existing, incoming) {
|
|
7656
7672
|
const existingBullets = existing ? parseBullets(existing) : [];
|
|
@@ -7660,14 +7676,18 @@ function deduplicateLearnedItems(existing, incoming) {
|
|
|
7660
7676
|
for (const bullet of incomingBullets) {
|
|
7661
7677
|
const norm = normalizeBullet(bullet);
|
|
7662
7678
|
if (!norm) continue;
|
|
7663
|
-
const
|
|
7679
|
+
const dupIdx = merged.findIndex((e) => {
|
|
7664
7680
|
const eNorm = normalizeBullet(e);
|
|
7665
7681
|
const shorter = Math.min(norm.length, eNorm.length);
|
|
7666
7682
|
const longer = Math.max(norm.length, eNorm.length);
|
|
7667
7683
|
if (!(eNorm.includes(norm) || norm.includes(eNorm))) return false;
|
|
7668
7684
|
return shorter / longer > 0.7;
|
|
7669
7685
|
});
|
|
7670
|
-
if (
|
|
7686
|
+
if (dupIdx !== -1) {
|
|
7687
|
+
if (hasTypePrefix(bullet) && !hasTypePrefix(merged[dupIdx])) {
|
|
7688
|
+
merged[dupIdx] = bullet;
|
|
7689
|
+
}
|
|
7690
|
+
} else {
|
|
7671
7691
|
merged.push(bullet);
|
|
7672
7692
|
newItems.push(bullet);
|
|
7673
7693
|
}
|
|
@@ -8137,6 +8157,17 @@ function appendEvent(event) {
|
|
|
8137
8157
|
fs30.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
8138
8158
|
}
|
|
8139
8159
|
}
|
|
8160
|
+
function appendPromptEvent(event) {
|
|
8161
|
+
ensureLearningDir();
|
|
8162
|
+
const filePath = sessionFilePath();
|
|
8163
|
+
fs30.appendFileSync(filePath, JSON.stringify(event) + "\n");
|
|
8164
|
+
const count = getEventCount();
|
|
8165
|
+
if (count > LEARNING_MAX_EVENTS) {
|
|
8166
|
+
const lines = fs30.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
8167
|
+
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
8168
|
+
fs30.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
8169
|
+
}
|
|
8170
|
+
}
|
|
8140
8171
|
function readAllEvents() {
|
|
8141
8172
|
const filePath = sessionFilePath();
|
|
8142
8173
|
if (!fs30.existsSync(filePath)) return [];
|
|
@@ -8197,14 +8228,6 @@ function acquireFinalizeLock() {
|
|
|
8197
8228
|
fs30.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
8198
8229
|
return true;
|
|
8199
8230
|
} catch {
|
|
8200
|
-
try {
|
|
8201
|
-
const stat = fs30.statSync(lockPath);
|
|
8202
|
-
if (Date.now() - stat.mtimeMs >= LOCK_STALE_MS) {
|
|
8203
|
-
fs30.writeFileSync(lockPath, String(process.pid));
|
|
8204
|
-
return true;
|
|
8205
|
-
}
|
|
8206
|
-
} catch {
|
|
8207
|
-
}
|
|
8208
8231
|
return false;
|
|
8209
8232
|
}
|
|
8210
8233
|
}
|
|
@@ -8216,17 +8239,64 @@ function releaseFinalizeLock() {
|
|
|
8216
8239
|
}
|
|
8217
8240
|
}
|
|
8218
8241
|
|
|
8242
|
+
// src/lib/sanitize.ts
|
|
8243
|
+
var KNOWN_PREFIX_PATTERNS = [
|
|
8244
|
+
// Anthropic (before generic sk- pattern)
|
|
8245
|
+
[/sk-ant-[A-Za-z0-9_-]{20,}/g, "[REDACTED]"],
|
|
8246
|
+
// AWS access key IDs
|
|
8247
|
+
[/AKIA[0-9A-Z]{16}/g, "[REDACTED]"],
|
|
8248
|
+
// AWS secret keys in assignments
|
|
8249
|
+
[/(?:aws)?_?secret_?(?:access)?_?key\s*[:=]\s*['"]?[A-Za-z0-9/+=]{40}['"]?/gi, "[REDACTED]"],
|
|
8250
|
+
// GitHub tokens (PAT, OAuth, server, app install, fine-grained)
|
|
8251
|
+
[/gh[pousr]_[A-Za-z0-9_]{36,}/g, "[REDACTED]"],
|
|
8252
|
+
[/github_pat_[A-Za-z0-9_]{22,}/g, "[REDACTED]"],
|
|
8253
|
+
// Stripe keys
|
|
8254
|
+
[/[sr]k_(live|test)_[A-Za-z0-9]{20,}/g, "[REDACTED]"],
|
|
8255
|
+
// Slack tokens
|
|
8256
|
+
[/xox[bpsar]-[A-Za-z0-9-]{10,}/g, "[REDACTED]"],
|
|
8257
|
+
// JWTs (3-segment base64url)
|
|
8258
|
+
[/eyJ[A-Za-z0-9_-]{20,}\.eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}/g, "[REDACTED]"],
|
|
8259
|
+
// OpenAI keys (after sk-ant- to avoid false match)
|
|
8260
|
+
[/sk-[A-Za-z0-9-]{20,}/g, "[REDACTED]"],
|
|
8261
|
+
// Google API keys
|
|
8262
|
+
[/AIza[A-Za-z0-9_-]{35}/g, "[REDACTED]"],
|
|
8263
|
+
// Bearer tokens
|
|
8264
|
+
[/[Bb]earer\s+[A-Za-z0-9_\-.]{20,}/g, "[REDACTED]"],
|
|
8265
|
+
// PEM private keys
|
|
8266
|
+
[/-----BEGIN[A-Z ]+KEY-----[\s\S]+?-----END[A-Z ]+KEY-----/g, "[REDACTED]"]
|
|
8267
|
+
];
|
|
8268
|
+
var SENSITIVE_ASSIGNMENT = /(?:api[_-]?key|secret[_-]?key|password|token|credential|auth[_-]?token|private[_-]?key)\s*[:=]\s*['"]?([^\s'"]{8,500})['"]?/gi;
|
|
8269
|
+
function sanitizeSecrets(text) {
|
|
8270
|
+
let result = text;
|
|
8271
|
+
for (const [pattern, replacement] of KNOWN_PREFIX_PATTERNS) {
|
|
8272
|
+
result = result.replace(pattern, replacement);
|
|
8273
|
+
}
|
|
8274
|
+
result = result.replace(
|
|
8275
|
+
SENSITIVE_ASSIGNMENT,
|
|
8276
|
+
(match, value) => match.replace(value, "[REDACTED]")
|
|
8277
|
+
);
|
|
8278
|
+
return result;
|
|
8279
|
+
}
|
|
8280
|
+
|
|
8219
8281
|
// src/ai/learn.ts
|
|
8220
8282
|
init_config();
|
|
8221
8283
|
var MAX_PROMPT_TOKENS = 1e5;
|
|
8222
8284
|
function formatEventsForPrompt(events) {
|
|
8223
8285
|
return events.map((e, i) => {
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8286
|
+
if (e.hook_event_name === "UserPromptSubmit") {
|
|
8287
|
+
const pe = e;
|
|
8288
|
+
return `--- Event ${i + 1} [USER_PROMPT] ---
|
|
8289
|
+
Time: ${pe.timestamp}
|
|
8290
|
+
User said:
|
|
8291
|
+
${pe.prompt_content}`;
|
|
8292
|
+
}
|
|
8293
|
+
const te = e;
|
|
8294
|
+
const status = te.hook_event_name === "PostToolUseFailure" ? "FAILURE" : "SUCCESS";
|
|
8295
|
+
const inputStr = JSON.stringify(te.tool_input, null, 2);
|
|
8296
|
+
const responseStr = typeof te.tool_response === "object" && "_truncated" in te.tool_response ? String(te.tool_response._truncated) : JSON.stringify(te.tool_response, null, 2);
|
|
8227
8297
|
return `--- Event ${i + 1} [${status}] ---
|
|
8228
|
-
Tool: ${
|
|
8229
|
-
Time: ${
|
|
8298
|
+
Tool: ${te.tool_name}
|
|
8299
|
+
Time: ${te.timestamp}
|
|
8230
8300
|
Input:
|
|
8231
8301
|
${inputStr}
|
|
8232
8302
|
Response:
|
|
@@ -8298,9 +8368,25 @@ async function learnObserveCommand(options) {
|
|
|
8298
8368
|
const raw = await readStdin();
|
|
8299
8369
|
if (!raw.trim()) return;
|
|
8300
8370
|
const hookData = JSON.parse(raw);
|
|
8371
|
+
const sessionId = hookData.session_id || hookData.conversation_id || "unknown";
|
|
8372
|
+
if (options.prompt) {
|
|
8373
|
+
const event2 = {
|
|
8374
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8375
|
+
session_id: sessionId,
|
|
8376
|
+
hook_event_name: "UserPromptSubmit",
|
|
8377
|
+
prompt_content: sanitizeSecrets(String(hookData.prompt_content || hookData.content || hookData.prompt || "")),
|
|
8378
|
+
cwd: hookData.cwd || process.cwd()
|
|
8379
|
+
};
|
|
8380
|
+
appendPromptEvent(event2);
|
|
8381
|
+
const state2 = readState2();
|
|
8382
|
+
state2.eventCount++;
|
|
8383
|
+
if (!state2.sessionId) state2.sessionId = sessionId;
|
|
8384
|
+
writeState2(state2);
|
|
8385
|
+
return;
|
|
8386
|
+
}
|
|
8301
8387
|
const event = {
|
|
8302
8388
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8303
|
-
session_id:
|
|
8389
|
+
session_id: sessionId,
|
|
8304
8390
|
hook_event_name: options.failure ? "PostToolUseFailure" : "PostToolUse",
|
|
8305
8391
|
tool_name: hookData.tool_name || "unknown",
|
|
8306
8392
|
tool_input: hookData.tool_input || {},
|
|
@@ -8311,7 +8397,7 @@ async function learnObserveCommand(options) {
|
|
|
8311
8397
|
appendEvent(event);
|
|
8312
8398
|
const state = readState2();
|
|
8313
8399
|
state.eventCount++;
|
|
8314
|
-
if (!state.sessionId) state.sessionId =
|
|
8400
|
+
if (!state.sessionId) state.sessionId = sessionId;
|
|
8315
8401
|
writeState2(state);
|
|
8316
8402
|
} catch {
|
|
8317
8403
|
}
|
|
@@ -8530,7 +8616,7 @@ program.command("score").description("Score your current agent config setup (det
|
|
|
8530
8616
|
program.command("refresh").description("Update docs based on recent code changes").option("--quiet", "Suppress output (for use in hooks)").option("--dry-run", "Preview changes without writing files").action(tracked("refresh", refreshCommand));
|
|
8531
8617
|
program.command("hooks").description("Manage auto-refresh hooks (toggle interactively)").option("--install", "Enable all hooks non-interactively").option("--remove", "Disable all hooks non-interactively").action(tracked("hooks", hooksCommand));
|
|
8532
8618
|
var learn = program.command("learn", { hidden: true }).description("[dev] Session learning \u2014 observe tool usage and extract reusable instructions");
|
|
8533
|
-
learn.command("observe").description("Record a tool event from stdin (called by hooks)").option("--failure", "Mark event as a tool failure").action(tracked("learn:observe", learnObserveCommand));
|
|
8619
|
+
learn.command("observe").description("Record a tool event from stdin (called by hooks)").option("--failure", "Mark event as a tool failure").option("--prompt", "Record a user prompt event").action(tracked("learn:observe", learnObserveCommand));
|
|
8534
8620
|
learn.command("finalize").description("Analyze session events and update CALIBER_LEARNINGS.md (called on SessionEnd)").option("--force", "Skip the running-process check (for manual invocation)").action(tracked("learn:finalize", (opts) => learnFinalizeCommand(opts)));
|
|
8535
8621
|
learn.command("install").description("Install learning hooks into .claude/settings.json").action(tracked("learn:install", learnInstallCommand));
|
|
8536
8622
|
learn.command("remove").description("Remove learning hooks from .claude/settings.json").action(tracked("learn:remove", learnRemoveCommand));
|
package/package.json
CHANGED