@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cachly-dev/mcp-server",
3
- "version": "0.9.15",
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.