@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.
@@ -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\'re about to hit:`);
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 rc = l.recall_count ?? 0;
1060
- const la = (l as { authors?: string[]; author?: string }).authors
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. Uses explicit focus when given; otherwise
1135
- // derives the likely work area from the last session's changed files +
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
- // Common path noise / extensions to drop when deriving terms from files.
1142
- const PATH_NOISE = new Set([
1143
- 'src', 'lib', 'test', 'tests', 'dist', 'index', 'main', 'app',
1144
- 'internal', 'pkg', 'cmd', 'node_modules', 'components', 'utils',
1145
- 'ts', 'tsx', 'js', 'jsx', 'go', 'py', 'rs', 'java', 'json', 'yaml', 'yml',
1146
- ]);
1147
- const deriveTermsFromFiles = (files: string[]): string[] => {
1148
- const terms = new Set<string>();
1149
- for (const f of files) {
1150
- for (const seg of f.toLowerCase().split(/[/\\._-]/)) {
1151
- if (seg.length > 3 && !PATH_NOISE.has(seg)) terms.add(seg);
1152
- }
1153
- }
1154
- return [...terms];
1155
- };
1156
-
1157
- let warnTerms = focusTerms;
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
 
@@ -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({instance_id:'${instance_id}',source:'ci_outcome',payload:{`);
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:'/api/v1/cls/ingest',method:'POST',`);
377
- lines.push(` headers:{'Content-Type':'application/json','Authorization':'Bearer \$CACHLY_JWT',`);
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(` CACHLY_JWT: \${{ secrets.CACHLY_JWT }}`);
382
+ lines.push(` CACHLY_CACHE_TOKEN: \${{ secrets.CACHLY_CACHE_TOKEN }}`);
382
383
  lines.push('```');
383
384
  lines.push('');
384
385
  }
@@ -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
- let items = Object.values(all)
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));
@@ -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_\-]{8,}/g, '[token]')
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