@cachly-dev/mcp-server 0.9.15 → 0.9.16
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/index.js +109 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +105 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cachly-dev/mcp-server",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.16",
|
|
4
4
|
"mcpName": "io.github.cachly-dev/cachly-mcp",
|
|
5
5
|
"description": "Your AI forgets everything between sessions. cachly fixes that permanently. A causal brain that learns WHY things break — built from every bug fix, git commit, and CI run, without any explicit calls. Arrives pre-briefed each session. Predicts failures before they happen. Never re-researches the same problem. 63 MCP tools · Claude Code · Cursor · GitHub Copilot · Windsurf · Free tier · EU servers.",
|
|
6
6
|
"type": "module",
|
package/src/index.ts
CHANGED
|
@@ -6133,6 +6133,111 @@ async function handleTool(name: string, args: Record<string, unknown>): Promise<
|
|
|
6133
6133
|
}
|
|
6134
6134
|
} catch { /* non-critical */ }
|
|
6135
6135
|
|
|
6136
|
+
// ── 🔮 PPE: Predictive Pre-fetch — CKG-powered risk detection ────────────
|
|
6137
|
+
// Layer 4: Before starting any work, scan the CKG for nodes matching focus
|
|
6138
|
+
// tokens and traverse causal edges to surface predicted failure points inline.
|
|
6139
|
+
// This is the PPE "pre-fetch" that was previously only available via brain_predict.
|
|
6140
|
+
if (focus && focus.length > 3) {
|
|
6141
|
+
try {
|
|
6142
|
+
const ppeFocusTokens = focus.toLowerCase().replace(/[^a-z0-9\s\-_:]/g, ' ').split(/\s+/).filter(t => t.length > 2).slice(0, 5);
|
|
6143
|
+
type PPEPrediction = { concept: string; edgeType: string; target: string; confidence: number; lesson?: { what_worked?: string; topic: string } };
|
|
6144
|
+
const ppePredictions: PPEPrediction[] = [];
|
|
6145
|
+
|
|
6146
|
+
for (const token of ppeFocusTokens) {
|
|
6147
|
+
const nodeKeys: string[] = [];
|
|
6148
|
+
const nStream = redis.scanStream({ match: `cachly:ckg:node:*${token}*`, count: 20 });
|
|
6149
|
+
await new Promise<void>((res, rej) => { nStream.on('data', (b: string[]) => nodeKeys.push(...b)); nStream.on('end', res); nStream.on('error', rej); });
|
|
6150
|
+
|
|
6151
|
+
for (const nk of nodeKeys.slice(0, 3)) {
|
|
6152
|
+
const nodeRaw = await redis.get(nk);
|
|
6153
|
+
if (!nodeRaw) continue;
|
|
6154
|
+
const node: CKGNode = JSON.parse(nodeRaw);
|
|
6155
|
+
const edgeKeys = await redis.smembers(`cachly:ckg:idx:from:${node.id}`);
|
|
6156
|
+
for (const ek of edgeKeys.slice(0, 15)) {
|
|
6157
|
+
const edgeRaw = await redis.get(ek);
|
|
6158
|
+
if (!edgeRaw) continue;
|
|
6159
|
+
const edge: CKGEdge = JSON.parse(edgeRaw);
|
|
6160
|
+
if (edge.edgeType !== 'causes' && edge.edgeType !== 'co-occurs' && edge.edgeType !== 'fixes') continue;
|
|
6161
|
+
if (edge.confidence < 0.35) continue;
|
|
6162
|
+
// For "fixes" edges, the lesson is on the "from" node (fix for "to")
|
|
6163
|
+
// For "causes" edges, target is the failure mode
|
|
6164
|
+
const lessonKey = edge.edgeType === 'fixes' ? `cachly:lesson:best:${edge.from}` : `cachly:lesson:best:${edge.to}`;
|
|
6165
|
+
const lessonRaw = await redis.get(lessonKey);
|
|
6166
|
+
const lesson = lessonRaw ? JSON.parse(lessonRaw) : undefined;
|
|
6167
|
+
ppePredictions.push({ concept: node.id, edgeType: edge.edgeType, target: edge.to, confidence: edge.confidence, lesson });
|
|
6168
|
+
}
|
|
6169
|
+
}
|
|
6170
|
+
}
|
|
6171
|
+
|
|
6172
|
+
const ppeSeen = new Set<string>();
|
|
6173
|
+
const ppeUniq = ppePredictions
|
|
6174
|
+
.filter(p => { const k = `${p.concept}:${p.target}`; if (ppeSeen.has(k)) return false; ppeSeen.add(k); return true; })
|
|
6175
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
6176
|
+
.slice(0, 4);
|
|
6177
|
+
|
|
6178
|
+
if (ppeUniq.length > 0) {
|
|
6179
|
+
lines.push(`🔮 **Predicted risks for "${focus}"** (PPE pre-fetch):`);
|
|
6180
|
+
for (const p of ppeUniq) {
|
|
6181
|
+
const confPct = Math.round(p.confidence * 100);
|
|
6182
|
+
const icon = p.edgeType === 'causes' ? '⚡' : p.edgeType === 'fixes' ? '🔧' : '🔄';
|
|
6183
|
+
lines.push(` ${icon} **${confPct}%** \`${p.concept}\` ${p.edgeType} \`${p.target}\``);
|
|
6184
|
+
if (p.lesson?.what_worked) lines.push(` ✅ ${(p.lesson.what_worked as string).slice(0, 110)}`);
|
|
6185
|
+
}
|
|
6186
|
+
lines.push(` _Full analysis: \`brain_predict(context="${focus}")\`_`);
|
|
6187
|
+
lines.push('');
|
|
6188
|
+
}
|
|
6189
|
+
} catch { /* non-critical — never block session start */ }
|
|
6190
|
+
}
|
|
6191
|
+
|
|
6192
|
+
// ── 📐 MCM Confidence Calibration — 30-day accuracy pass ─────────────────
|
|
6193
|
+
// Layer 7: Periodically check if high-confidence lessons are actually reliable.
|
|
6194
|
+
// If last calibration was >30 days ago (or never), run a quick pass.
|
|
6195
|
+
// Measures: of recalled lessons with confidence > 0.85, what % had outcome=success?
|
|
6196
|
+
try {
|
|
6197
|
+
const calRaw = await redis.get('cachly:mcm:calibration:last');
|
|
6198
|
+
const lastCalMs = calRaw ? JSON.parse(calRaw).ts : 0;
|
|
6199
|
+
const daysSinceCal = (Date.now() - lastCalMs) / 86_400_000;
|
|
6200
|
+
if (daysSinceCal >= 30 && lessons.length >= 5) {
|
|
6201
|
+
// Quick calibration pass on recalled lessons (recall_count > 0, outcome=success)
|
|
6202
|
+
const recalledSuccess = lessons.filter(l => l.outcome === 'success' && (l.recall_count ?? 0) > 0);
|
|
6203
|
+
const recalledAll = lessons.filter(l => (l.recall_count ?? 0) > 0);
|
|
6204
|
+
if (recalledAll.length >= 3) {
|
|
6205
|
+
const precision = recalledAll.length > 0 ? recalledSuccess.length / recalledAll.length : 1;
|
|
6206
|
+
const lowPrecisionDomains: string[] = [];
|
|
6207
|
+
// Per-domain breakdown
|
|
6208
|
+
const domCalMap = new Map<string, { success: number; total: number }>();
|
|
6209
|
+
for (const l of recalledAll) {
|
|
6210
|
+
const dom = l.topic.split(':')[0] ?? 'other';
|
|
6211
|
+
if (!domCalMap.has(dom)) domCalMap.set(dom, { success: 0, total: 0 });
|
|
6212
|
+
const d = domCalMap.get(dom)!;
|
|
6213
|
+
d.total++;
|
|
6214
|
+
if (l.outcome === 'success') d.success++;
|
|
6215
|
+
}
|
|
6216
|
+
for (const [dom, d] of domCalMap) {
|
|
6217
|
+
if (d.total >= 2 && d.success / d.total < 0.6) lowPrecisionDomains.push(dom);
|
|
6218
|
+
}
|
|
6219
|
+
// Save calibration result
|
|
6220
|
+
await redis.set('cachly:mcm:calibration:last', JSON.stringify({
|
|
6221
|
+
ts: Date.now(),
|
|
6222
|
+
precision: precision,
|
|
6223
|
+
recalled: recalledAll.length,
|
|
6224
|
+
low_precision_domains: lowPrecisionDomains,
|
|
6225
|
+
}));
|
|
6226
|
+
if (lowPrecisionDomains.length > 0 || precision < 0.7) {
|
|
6227
|
+
lines.push(`📐 **MCM Calibration** (30-day pass — ${recalledAll.length} recalled lessons):`);
|
|
6228
|
+
lines.push(` Overall precision: **${(precision * 100).toFixed(0)}%** recalled lessons actually worked`);
|
|
6229
|
+
if (lowPrecisionDomains.length > 0) {
|
|
6230
|
+
lines.push(` ⚠️ Low-precision domains: ${lowPrecisionDomains.map(d => `\`${d}\``).join(', ')} — consider revisiting these lessons`);
|
|
6231
|
+
}
|
|
6232
|
+
lines.push(` 💡 Use \`learn_from_attempts\` to update stale lessons and improve accuracy.`);
|
|
6233
|
+
lines.push('');
|
|
6234
|
+
}
|
|
6235
|
+
}
|
|
6236
|
+
// Even if no issues, stamp the date so we don't re-check for 30d
|
|
6237
|
+
await redis.set('cachly:mcm:calibration:last', JSON.stringify({ ts: Date.now(), precision: 1, recalled: 0 }), 'EX', 35 * 86400);
|
|
6238
|
+
}
|
|
6239
|
+
} catch { /* non-critical */ }
|
|
6240
|
+
|
|
6136
6241
|
// ── 🌍 Knowledge Commons — community stats banner ───────────────────────
|
|
6137
6242
|
// Fetch syndication stats and show a 1-liner: total lessons + confirms + weekly growth.
|
|
6138
6243
|
// Non-fatal: never blocks session start if the API call fails.
|