@cachly-dev/mcp-server 0.10.46 โ 0.10.48
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 +33 -5
- package/dist/__tests__/brain-flow.test.js +2 -143
- package/dist/__tests__/brain-flow.test.js.map +1 -1
- package/dist/__tests__/brain-predict-failures.test.d.ts +17 -0
- package/dist/__tests__/brain-predict-failures.test.js +339 -0
- package/dist/__tests__/brain-predict-failures.test.js.map +1 -0
- package/dist/__tests__/handlers-unit.test.js +3 -1
- package/dist/__tests__/handlers-unit.test.js.map +1 -1
- package/dist/handlers/brain.js +22 -121
- package/dist/handlers/brain.js.map +1 -1
- package/dist/handlers/fedbrain.js +6 -5
- package/dist/handlers/fedbrain.js.map +1 -1
- package/dist/handlers/roadmap.js +1 -1
- package/dist/handlers/roadmap.js.map +1 -1
- package/dist/handlers/team.js +1 -1
- package/dist/handlers/team.js.map +1 -1
- package/dist/index.js +201 -42
- package/dist/index.js.map +1 -1
- package/dist/search.js +3 -3
- package/dist/search.js.map +1 -1
- package/package.json +2 -1
- package/smithery.yaml +8 -21
- package/src/__tests__/brain-flow.test.ts +2 -176
- package/src/__tests__/brain-predict-failures.test.ts +430 -0
- package/src/__tests__/handlers-unit.test.ts +2 -1
- package/src/handlers/brain.ts +23 -125
- package/src/handlers/fedbrain.ts +6 -5
- package/src/handlers/roadmap.ts +1 -1
- package/src/handlers/team.ts +1 -1
- package/src/index.ts +204 -41
- package/src/search.ts +3 -3
package/src/handlers/brain.ts
CHANGED
|
@@ -417,27 +417,8 @@ export async function handleBrainTool(
|
|
|
417
417
|
? `๐ **You've looked this up 3 times.** Consider pinning it for instant access: add \`pinned: true\` via \`learn_from_attempts\` to always surface it first.`
|
|
418
418
|
: '';
|
|
419
419
|
|
|
420
|
-
// โโ Trust signal (today-safe consensus layer) โโโโโโโโโโโโโโโโโโโโโโโ
|
|
421
|
-
// A lesson recalled many times โ or confirmed by multiple distinct
|
|
422
|
-
// authors โ has proven its value. Surface that as social proof.
|
|
423
|
-
const lessonAuthors = (lesson as { authors?: string[]; author?: string }).authors
|
|
424
|
-
?? ((lesson as { author?: string }).author ? [(lesson as { author?: string }).author!] : []);
|
|
425
|
-
const distinctAuthors = [...new Set(lessonAuthors.filter(Boolean))];
|
|
426
|
-
const rc = updatedLesson.recall_count;
|
|
427
|
-
let trustBadge = '';
|
|
428
|
-
if (distinctAuthors.length >= 2 && rc >= 5) {
|
|
429
|
-
trustBadge = `๐ **Battle-tested** โ recalled ${rc}ร ยท verified by ${distinctAuthors.length} developers. Trust this.`;
|
|
430
|
-
} else if (rc >= 10) {
|
|
431
|
-
trustBadge = `๐ **Battle-tested** โ recalled ${rc}ร. This is one of your most-proven solutions.`;
|
|
432
|
-
} else if (distinctAuthors.length >= 2) {
|
|
433
|
-
trustBadge = `โ
**Team-verified** โ confirmed by ${distinctAuthors.length} developers.`;
|
|
434
|
-
} else if (rc >= 5) {
|
|
435
|
-
trustBadge = `โ
**Proven** โ recalled ${rc}ร without contradiction.`;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
420
|
return [
|
|
439
421
|
rememberWhen,
|
|
440
|
-
trustBadge,
|
|
441
422
|
`${badge} **Best solution for \`${topic}\`** ${sevEmoji}${lesson.severity ? ` (${lesson.severity})` : ''} ยท recalled ${updatedLesson.recall_count}ร`,
|
|
442
423
|
``,
|
|
443
424
|
`**What worked:** ${lesson.what_worked}`,
|
|
@@ -502,22 +483,6 @@ export async function handleBrainTool(
|
|
|
502
483
|
10,
|
|
503
484
|
);
|
|
504
485
|
|
|
505
|
-
// Increment recall_count on matched lessons (fire-and-forget โ same as recall_best_solution).
|
|
506
|
-
// This ensures the dashboard metric, Proven Laws, and trust badges reflect real smart_recall usage.
|
|
507
|
-
const lessonMatches = kwMatches.filter(m => m.key.startsWith('cachly:lesson:best:'));
|
|
508
|
-
for (const m of lessonMatches.slice(0, 5)) {
|
|
509
|
-
const existing = await redis.get(m.key).catch(() => null);
|
|
510
|
-
if (existing) {
|
|
511
|
-
const lesson = safeJsonParse(existing, null as null | { recall_count?: number; [k: string]: unknown });
|
|
512
|
-
if (lesson) {
|
|
513
|
-
const updated = { ...lesson, recall_count: (lesson.recall_count ?? 0) + 1, verified_at: new Date().toISOString() };
|
|
514
|
-
redis.set(m.key, JSON.stringify(updated)).catch(() => {});
|
|
515
|
-
const savedMins = (lesson.severity as string) === 'critical' ? 240 : (lesson.severity as string) === 'major' ? 60 : 30;
|
|
516
|
-
redis.incrbyfloat(`cachly:stats:time_saved_mins:${instance_id}`, savedMins).catch(() => {});
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
486
|
const lines: string[] = [`๐ง **Smart Recall** for: _"${query}"_\n`];
|
|
522
487
|
|
|
523
488
|
// Show sub-query info if multi-topic was detected
|
|
@@ -813,7 +778,7 @@ export async function handleBrainTool(
|
|
|
813
778
|
lines.push('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
|
|
814
779
|
lines.push('๐ค **Your team\'s AI brain has been briefing you.**');
|
|
815
780
|
lines.push('');
|
|
816
|
-
lines.push(`Your teammates have already solved problems you
|
|
781
|
+
lines.push(`Your teammates have already solved problems you're about to hit:`);
|
|
817
782
|
lines.push('');
|
|
818
783
|
for (const [teamAuthor, tls] of byAuthor) {
|
|
819
784
|
lines.push(` ๐ค **${teamAuthor}** fixed ${tls.length} thing${tls.length > 1 ? 's' : ''}:`);
|
|
@@ -932,31 +897,6 @@ export async function handleBrainTool(
|
|
|
932
897
|
// Brain health
|
|
933
898
|
lines.push(`๐ **Brain:** ${lessons.length} lessons ยท ${ctxCount} context entries`, '');
|
|
934
899
|
|
|
935
|
-
// โโ ๐ Proven Laws โ auto-crystallized lessons (quick win 3) โโโโโโโโโโโโโ
|
|
936
|
-
// A lesson that has proven itself (recalled โฅ CRYSTALLIZE_RECALLS times) or
|
|
937
|
-
// was explicitly pinned becomes a "law" โ surfaced at the top of every
|
|
938
|
-
// briefing so the most battle-tested knowledge is never buried. This is
|
|
939
|
-
// the today-safe version of swarm crystallization: enough confirmations
|
|
940
|
-
// auto-promote a lesson without any manual step.
|
|
941
|
-
{
|
|
942
|
-
type LessonPin = typeof lessons[0] & { pinned?: boolean };
|
|
943
|
-
const CRYSTALLIZE_RECALLS = 5;
|
|
944
|
-
const laws = (lessons as LessonPin[])
|
|
945
|
-
.filter(l => l.outcome === 'success' && (l.pinned === true || (l.recall_count ?? 0) >= CRYSTALLIZE_RECALLS))
|
|
946
|
-
.sort((a, b) => (b.recall_count ?? 0) - (a.recall_count ?? 0))
|
|
947
|
-
.slice(0, 5);
|
|
948
|
-
if (laws.length > 0) {
|
|
949
|
-
lines.push(`๐ **Proven Laws** (auto-crystallized โ your most-trusted knowledge):`);
|
|
950
|
-
for (const l of laws) {
|
|
951
|
-
const sev = l.severity === 'critical' ? '๐ด' : l.severity === 'major' ? '๐ก' : '';
|
|
952
|
-
const rc = l.recall_count ?? 0;
|
|
953
|
-
const why = l.pinned === true ? 'pinned' : `recalled ${rc}ร`;
|
|
954
|
-
lines.push(` ๐${sev} \`${l.topic}\` _(${why})_ โ ${l.what_worked.slice(0, 100)}`);
|
|
955
|
-
}
|
|
956
|
-
lines.push('');
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
|
|
960
900
|
// โโ Layer 7: MCM Domain Coverage Map โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
961
901
|
if (lessons.length >= 3) {
|
|
962
902
|
const domainMap = new Map<string, { total: number; success: number; critical: number }>();
|
|
@@ -1056,14 +996,8 @@ export async function handleBrainTool(
|
|
|
1056
996
|
for (const l of topRecalled) {
|
|
1057
997
|
const emoji = l.outcome === 'success' ? 'โ
' : l.outcome === 'partial' ? 'โ ๏ธ' : 'โ';
|
|
1058
998
|
const sev = l.severity === 'critical' ? '๐ด' : l.severity === 'major' ? '๐ก' : '';
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
?? ((l as { author?: string }).author ? [(l as { author?: string }).author!] : []);
|
|
1062
|
-
const nAuthors = new Set(la.filter(Boolean)).size;
|
|
1063
|
-
const trust = (nAuthors >= 2 && rc >= 5) || rc >= 10 ? ' ๐'
|
|
1064
|
-
: nAuthors >= 2 || rc >= 5 ? ' โ
' : '';
|
|
1065
|
-
const rct = rc > 1 ? ` _(${rc}ร recalled)_` : '';
|
|
1066
|
-
lines.push(` ${emoji}${sev}${trust} \`${l.topic}\`${rct} โ ${l.what_worked.slice(0, 100)}`);
|
|
999
|
+
const rct = l.recall_count && l.recall_count > 1 ? ` _(${l.recall_count}ร recalled)_` : '';
|
|
1000
|
+
lines.push(` ${emoji}${sev} \`${l.topic}\`${rct} โ ${l.what_worked.slice(0, 100)}`);
|
|
1067
1001
|
}
|
|
1068
1002
|
lines.push('');
|
|
1069
1003
|
}
|
|
@@ -1131,63 +1065,27 @@ export async function handleBrainTool(
|
|
|
1131
1065
|
}
|
|
1132
1066
|
|
|
1133
1067
|
// โโ ๐ฎ Predictive Pre-Warning โ intent-based danger detection โโโโโโโโโโโโ
|
|
1134
|
-
// Fires BEFORE work starts.
|
|
1135
|
-
|
|
1136
|
-
// summary (you usually keep working where you left off). This makes the
|
|
1137
|
-
// warning fire even when the caller forgets to pass `focus`.
|
|
1138
|
-
{
|
|
1068
|
+
// Fires BEFORE work starts. If focus area has known failure patterns โ warn loudly.
|
|
1069
|
+
if (focusTerms.length > 0) {
|
|
1139
1070
|
type LessonAny = typeof lessons[0] & { author?: string; tags?: string[] };
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
let warnLabel = focus;
|
|
1159
|
-
let derived = false;
|
|
1160
|
-
if (warnTerms.length === 0 && lastSession) {
|
|
1161
|
-
const fromFiles = deriveTermsFromFiles(lastSession.files_changed ?? []);
|
|
1162
|
-
const fromSummary = (lastSession.summary ?? '').toLowerCase()
|
|
1163
|
-
.replace(/[^a-z0-9\s:_-]/g, ' ').split(/\s+/).filter(t => t.length > 3 && !PATH_NOISE.has(t));
|
|
1164
|
-
warnTerms = [...new Set([...fromFiles, ...fromSummary])];
|
|
1165
|
-
warnLabel = 'where you left off last session';
|
|
1166
|
-
derived = true;
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
if (warnTerms.length > 0) {
|
|
1170
|
-
const dangerLessons = (lessons as LessonAny[]).filter(l => {
|
|
1171
|
-
if (l.outcome === 'success') return false;
|
|
1172
|
-
const topicCategory = l.topic.split(':')[0];
|
|
1173
|
-
return warnTerms.some(term =>
|
|
1174
|
-
l.topic.toLowerCase().includes(term) ||
|
|
1175
|
-
topicCategory === term ||
|
|
1176
|
-
(l.tags ?? []).some((t: string) => t.toLowerCase() === term),
|
|
1177
|
-
);
|
|
1178
|
-
});
|
|
1179
|
-
if (dangerLessons.length >= 1) {
|
|
1180
|
-
const headline = derived
|
|
1181
|
-
? ` You're likely to continue **${warnLabel}** โ ${dangerLessons.length} known pitfall${dangerLessons.length > 1 ? 's' : ''} there:`
|
|
1182
|
-
: ` Known pitfalls for **"${warnLabel}"** (${dangerLessons.length} past failure${dangerLessons.length > 1 ? 's' : ''}):`;
|
|
1183
|
-
const warning = [
|
|
1184
|
-
`๐จ **PRE-WARNING** โ Read this BEFORE starting:`,
|
|
1185
|
-
headline,
|
|
1186
|
-
...dangerLessons.slice(0, 3).map(l => ` โ \`${l.topic}\` โ ${(l.what_failed ?? l.what_worked).slice(0, 80)}`),
|
|
1187
|
-
'',
|
|
1188
|
-
];
|
|
1189
|
-
lines.splice(2, 0, ...warning); // after '๐ง **Session Briefing**' + empty line
|
|
1190
|
-
}
|
|
1071
|
+
const dangerLessons = (lessons as LessonAny[]).filter(l => {
|
|
1072
|
+
if (l.outcome === 'success') return false;
|
|
1073
|
+
const topicCategory = l.topic.split(':')[0];
|
|
1074
|
+
return focusTerms.some(term =>
|
|
1075
|
+
l.topic.toLowerCase().includes(term) ||
|
|
1076
|
+
topicCategory === term ||
|
|
1077
|
+
(l.tags ?? []).some((t: string) => t.toLowerCase() === term),
|
|
1078
|
+
);
|
|
1079
|
+
});
|
|
1080
|
+
if (dangerLessons.length >= 1) {
|
|
1081
|
+
// Insert warning block right after the title line (index 1 = blank line after title)
|
|
1082
|
+
const warning = [
|
|
1083
|
+
`๐จ **PRE-WARNING** โ Read this BEFORE starting:`,
|
|
1084
|
+
` Known pitfalls for **"${focus}"** (${dangerLessons.length} past failure${dangerLessons.length > 1 ? 's' : ''}):`,
|
|
1085
|
+
...dangerLessons.slice(0, 3).map(l => ` โ \`${l.topic}\` โ ${(l.what_failed ?? l.what_worked).slice(0, 80)}`),
|
|
1086
|
+
'',
|
|
1087
|
+
];
|
|
1088
|
+
lines.splice(2, 0, ...warning); // after '๐ง **Session Briefing**' + empty line
|
|
1191
1089
|
}
|
|
1192
1090
|
}
|
|
1193
1091
|
|
package/src/handlers/fedbrain.ts
CHANGED
|
@@ -363,6 +363,8 @@ export async function handleFedbrainTool(
|
|
|
363
363
|
|
|
364
364
|
if (hooksArr.includes('ci')) {
|
|
365
365
|
lines.push(`### GitHub Actions CI outcome hook`);
|
|
366
|
+
lines.push(`**Setup:** add a repo secret \`CACHLY_CACHE_TOKEN\` = your instance's cache token`);
|
|
367
|
+
lines.push(`(find it via \`get_connection_string\` โ the token in the cache URL).`);
|
|
366
368
|
lines.push(`**Add at the end of each job** (after build/test steps):`);
|
|
367
369
|
lines.push('```yaml');
|
|
368
370
|
lines.push(`- name: cachly CLS โ record CI outcome`);
|
|
@@ -370,15 +372,14 @@ export async function handleFedbrainTool(
|
|
|
370
372
|
lines.push(` run: |`);
|
|
371
373
|
lines.push(` node -e "`);
|
|
372
374
|
lines.push(` const r=require('https');`);
|
|
373
|
-
lines.push(` const d=JSON.stringify({
|
|
375
|
+
lines.push(` const d=JSON.stringify({source:'ci_outcome',payload:{`);
|
|
374
376
|
lines.push(` status:'\${{ job.status }}',prev_status:'unknown',job:'\${{ github.job }}',`);
|
|
375
377
|
lines.push(` context:'github-actions run \${{ github.run_number }}'}});`);
|
|
376
|
-
lines.push(` r.request({hostname:'api.cachly.dev',path:'/
|
|
377
|
-
lines.push(` headers:{'Content-Type':'application/json','
|
|
378
|
-
lines.push(` 'Content-Length':d.length}},()=>{}).end(d);`);
|
|
378
|
+
lines.push(` r.request({hostname:'api.cachly.dev',path:'/v1/cache/'+process.env.CACHLY_CACHE_TOKEN+'/cls/ingest',method:'POST',`);
|
|
379
|
+
lines.push(` headers:{'Content-Type':'application/json','Content-Length':d.length}},()=>{}).end(d);`);
|
|
379
380
|
lines.push(` " 2>/dev/null || true`);
|
|
380
381
|
lines.push(` env:`);
|
|
381
|
-
lines.push(`
|
|
382
|
+
lines.push(` CACHLY_CACHE_TOKEN: \${{ secrets.CACHLY_CACHE_TOKEN }}`);
|
|
382
383
|
lines.push('```');
|
|
383
384
|
lines.push('');
|
|
384
385
|
}
|
package/src/handlers/roadmap.ts
CHANGED
|
@@ -156,7 +156,7 @@ export async function handleRoadmapTool(
|
|
|
156
156
|
}
|
|
157
157
|
const PRIORITY_ORDER: Record<string, number> = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
158
158
|
const PRIORITY_ICON: Record<string, string> = { critical: '๐ด', high: '๐ ', medium: '๐ก', low: '๐ต' };
|
|
159
|
-
|
|
159
|
+
const items = Object.values(all)
|
|
160
160
|
.map(v => JSON.parse(v as string) as Record<string, unknown>)
|
|
161
161
|
.filter(i => i.status === 'in-progress' || i.status === 'planned')
|
|
162
162
|
.filter(i => !filterTag || (i.tags as string[]).includes(filterTag));
|
package/src/handlers/team.ts
CHANGED
|
@@ -709,7 +709,7 @@ export async function handleTeamTool(
|
|
|
709
709
|
// Strip potential PII patterns (emails, tokens, paths)
|
|
710
710
|
const sanitized = lesson
|
|
711
711
|
.replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, '[email]')
|
|
712
|
-
.replace(/\b(sk-|cky_live_|Bearer\s)[A-Za-z0-9_
|
|
712
|
+
.replace(/\b(sk-|cky_live_|Bearer\s)[A-Za-z0-9_-]{8,}/g, '[token]')
|
|
713
713
|
.replace(/\/Users\/[^\s/]+/g, '/Users/[user]')
|
|
714
714
|
.replace(/\/home\/[^\s/]+/g, '/home/[user]');
|
|
715
715
|
|