@cachly-dev/mcp-server 0.10.14 → 0.10.15
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/.github/ISSUE_TEMPLATE/bug_report.md +50 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- package/.github/workflows/publish.yml +72 -0
- package/CHANGELOG.md +28 -0
- package/LAUNCH_TEXTS.md +182 -0
- package/LICENSE +202 -0
- package/README.md +113 -112
- package/dist/__tests__/auth.test.d.ts +1 -0
- package/dist/__tests__/auth.test.js +107 -0
- package/dist/__tests__/auth.test.js.map +1 -0
- package/dist/handlers/brain.js +7 -67
- package/dist/handlers/brain.js.map +1 -1
- package/dist/handlers/fedbrain.js +93 -44
- package/dist/handlers/fedbrain.js.map +1 -1
- package/dist/handlers/roadmap.js +8 -2
- package/dist/handlers/roadmap.js.map +1 -1
- package/dist/handlers/syndicate.js +15 -7
- package/dist/handlers/syndicate.js.map +1 -1
- package/dist/handlers/team.js +62 -50
- package/dist/handlers/team.js.map +1 -1
- package/dist/index.js +555 -93
- package/dist/index.js.map +1 -1
- package/dist/tools.d.ts +3 -54
- package/dist/tools.js +8 -62
- package/dist/tools.js.map +1 -1
- package/glama.json +29 -0
- package/llms.txt +155 -0
- package/package.json +29 -13
- package/scripts/postinstall.js +12 -12
- package/server.json +3 -9
- package/smithery.yaml +6 -8
- package/src/__tests__/auth.test.ts +147 -0
- package/src/handlers/brain.ts +7 -64
- package/src/handlers/fedbrain.ts +88 -38
- package/src/handlers/roadmap.ts +7 -2
- package/src/handlers/syndicate.ts +13 -7
- package/src/handlers/team.ts +57 -33
- package/src/index.ts +599 -106
- package/src/tools.ts +8 -66
- package/dist/__tests__/brain-predict-failures.test.d.ts +0 -17
- package/dist/__tests__/brain-predict-failures.test.js +0 -339
- package/dist/__tests__/brain-predict-failures.test.js.map +0 -1
- package/src/__tests__/brain-predict-failures.test.ts +0 -430
package/src/handlers/team.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { Redis } from 'ioredis';
|
|
|
5
5
|
import { calculateConfidence, confidenceBadge, CONFIDENCE_STALE_VALUE, CONFIDENCE_WARN_VALUE,
|
|
6
6
|
CONFIDENCE_WARN_DAYS, CONFIDENCE_STALE_DAYS, simpleHash } from '../confidence.js';
|
|
7
7
|
import type { Instance } from './brain.js';
|
|
8
|
+
import { safeJsonParse } from '../utils.js';
|
|
8
9
|
|
|
9
10
|
type GetConnection = (instanceId: string) => Promise<Redis>;
|
|
10
11
|
type ApiFetch = <T>(path: string, options?: RequestInit) => Promise<T>;
|
|
@@ -53,10 +54,11 @@ export async function handleTeamTool(
|
|
|
53
54
|
|
|
54
55
|
type Lesson = { topic: string; what_worked: string; outcome: string; file_paths?: string[] };
|
|
55
56
|
const relevant: string[] = [];
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
const sfRaws = lessonKeys.length > 0 ? await redis.mget(...lessonKeys) : [];
|
|
58
|
+
for (const raw of sfRaws) {
|
|
58
59
|
if (!raw) continue;
|
|
59
|
-
const lesson =
|
|
60
|
+
const lesson = safeJsonParse<Lesson | null>(raw, null);
|
|
61
|
+
if (!lesson) continue;
|
|
60
62
|
// Match by file_paths stored in lesson OR by topic keywords matching file name
|
|
61
63
|
const topicWords = lesson.topic.toLowerCase().split(/[:\-_]/);
|
|
62
64
|
const fileMatches = changed_files.some(f => {
|
|
@@ -146,10 +148,12 @@ export async function handleTeamTool(
|
|
|
146
148
|
author?: string; tags?: string[];
|
|
147
149
|
};
|
|
148
150
|
let lessons: TeamLesson[] = [];
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
if (lessonKeys.length > 0) {
|
|
152
|
+
const raws = await redis.mget(...lessonKeys);
|
|
153
|
+
for (const raw of raws) {
|
|
154
|
+
const l = safeJsonParse<TeamLesson | null>(raw ?? null, null);
|
|
155
|
+
if (l) lessons.push(l);
|
|
156
|
+
}
|
|
153
157
|
}
|
|
154
158
|
|
|
155
159
|
// Filter
|
|
@@ -278,10 +282,12 @@ export async function handleTeamTool(
|
|
|
278
282
|
|
|
279
283
|
type RawLesson = { topic: string; outcome: string; what_worked: string; severity?: string; ts: string; auto_learned?: boolean };
|
|
280
284
|
const allLessons: RawLesson[] = [];
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
285
|
+
if (allLessonKeys.length > 0) {
|
|
286
|
+
const raws = await redis.mget(...allLessonKeys);
|
|
287
|
+
for (const raw of raws) {
|
|
288
|
+
const l = safeJsonParse<RawLesson | null>(raw ?? null, null);
|
|
289
|
+
if (l) allLessons.push(l);
|
|
290
|
+
}
|
|
285
291
|
}
|
|
286
292
|
|
|
287
293
|
// Group lessons by top-level category
|
|
@@ -367,18 +373,17 @@ export async function handleTeamTool(
|
|
|
367
373
|
verified_at?: string; severity?: string; audit_trail?: unknown[];
|
|
368
374
|
};
|
|
369
375
|
const lessons: DrLesson[] = [];
|
|
370
|
-
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
376
|
+
if (lessonKeys.length > 0) {
|
|
377
|
+
const raws = await redis.mget(...lessonKeys);
|
|
378
|
+
for (const raw of raws) {
|
|
379
|
+
const l = safeJsonParse<DrLesson | null>(raw ?? null, null);
|
|
380
|
+
if (l) lessons.push(l);
|
|
381
|
+
}
|
|
374
382
|
}
|
|
375
383
|
|
|
376
384
|
// Last session
|
|
377
385
|
const lastSessionRaw = await redis.get('cachly:session:last');
|
|
378
|
-
|
|
379
|
-
if (lastSessionRaw) {
|
|
380
|
-
try { lastSession = JSON.parse(lastSessionRaw); } catch { /* ignore */ }
|
|
381
|
-
}
|
|
386
|
+
const lastSession = safeJsonParse<{ ts: string; summary: string } | null>(lastSessionRaw, null);
|
|
382
387
|
|
|
383
388
|
// Open failures
|
|
384
389
|
const openFailures = lessons.filter(l => l.outcome === 'failure' || l.outcome === 'partial');
|
|
@@ -509,13 +514,24 @@ export async function handleTeamTool(
|
|
|
509
514
|
issues.push(`💡 ${unusedLessons.length} lessons never recalled — verify topics match your workflow`);
|
|
510
515
|
}
|
|
511
516
|
|
|
512
|
-
const
|
|
517
|
+
const divider = '─'.repeat(48);
|
|
518
|
+
const lines = [
|
|
519
|
+
`🩺 **Brain Doctor**`,
|
|
520
|
+
`\`${divider}\``,
|
|
521
|
+
'',
|
|
522
|
+
'**📊 Health Checks**',
|
|
523
|
+
...checks.map(c => ' ' + c),
|
|
524
|
+
'',
|
|
525
|
+
];
|
|
513
526
|
if (issues.length > 0) {
|
|
514
|
-
lines.push(
|
|
515
|
-
|
|
527
|
+
lines.push(`**🔧 Action Items** (${issues.length} issue${issues.length > 1 ? 's' : ''})`);
|
|
528
|
+
issues.forEach((item, idx) => lines.push(` ${idx + 1}. ${item}`));
|
|
516
529
|
lines.push('');
|
|
530
|
+
lines.push(`_Fix the action items above, then re-run \`brain_doctor\` to verify._`);
|
|
517
531
|
} else {
|
|
518
|
-
lines.push('
|
|
532
|
+
lines.push('**✅ All checks passed** — Brain is healthy!');
|
|
533
|
+
lines.push('');
|
|
534
|
+
lines.push('_Keep the brain sharp: `session_start` → work → `learn_from_attempts` → `session_end`_');
|
|
519
535
|
}
|
|
520
536
|
return lines.join('\n');
|
|
521
537
|
}
|
|
@@ -536,7 +552,11 @@ export async function handleTeamTool(
|
|
|
536
552
|
.filter((l): l is NonNullable<typeof l> => l !== null && new Date(l.ts).getTime() <= cutoff)
|
|
537
553
|
.sort((a, b) => new Date(a.ts).getTime() - new Date(b.ts).getTime());
|
|
538
554
|
|
|
539
|
-
if (before.length === 0)
|
|
555
|
+
if (before.length === 0) {
|
|
556
|
+
const first = safeJsonParse(all[0] ?? null, null as null | { ts?: string });
|
|
557
|
+
const earliest = first?.ts ? new Date(first.ts).toLocaleDateString('de-DE') : 'unknown';
|
|
558
|
+
return `📭 No entries for \`${topic}\` found before **${date}**. Earliest entry: ${earliest}.`;
|
|
559
|
+
}
|
|
540
560
|
|
|
541
561
|
const lines = [
|
|
542
562
|
`🏺 **Brain Archaeology: \`${topic}\` before ${date}**`,
|
|
@@ -563,7 +583,7 @@ export async function handleTeamTool(
|
|
|
563
583
|
const raw = await redis.get(depKey);
|
|
564
584
|
if (!raw) return `📭 No lessons found that depend on \`${dependency}\`.\n\nAdd dependencies via: \`learn_from_attempts(..., depends_on=["${dependency}"])\``;
|
|
565
585
|
|
|
566
|
-
const topics: string[] =
|
|
586
|
+
const topics: string[] = safeJsonParse<string[]>(raw, []);
|
|
567
587
|
const lines = [
|
|
568
588
|
`🔗 **Causal Chain: \`${dependency}\`** — ${topics.length} dependent lesson${topics.length === 1 ? '' : 's'}`,
|
|
569
589
|
'',
|
|
@@ -572,7 +592,8 @@ export async function handleTeamTool(
|
|
|
572
592
|
for (const t of topics) {
|
|
573
593
|
const lessonRaw = await redis.get(`cachly:lesson:best:${t}`);
|
|
574
594
|
if (!lessonRaw) { lines.push(` • \`${t}\` _(lesson deleted)_`); continue; }
|
|
575
|
-
const lesson =
|
|
595
|
+
const lesson = safeJsonParse(lessonRaw, null as null | { outcome?: string; severity?: string; needs_review?: boolean });
|
|
596
|
+
if (!lesson) { lines.push(` • \`${t}\` _(lesson corrupted)_`); continue; }
|
|
576
597
|
const emoji = lesson.outcome === 'success' ? '✅' : lesson.outcome === 'partial' ? '⚠️' : '❌';
|
|
577
598
|
const reviewBadge = lesson.needs_review ? ' 🔍 **needs_review**' : '';
|
|
578
599
|
lines.push(` ${emoji} \`${t}\` (${lesson.severity ?? 'major'})${reviewBadge}`);
|
|
@@ -614,8 +635,8 @@ export async function handleTeamTool(
|
|
|
614
635
|
// Preserve recall_count on update
|
|
615
636
|
const existing = await redis.get(key);
|
|
616
637
|
if (existing) {
|
|
617
|
-
const prev =
|
|
618
|
-
record.recall_count = prev
|
|
638
|
+
const prev = safeJsonParse(existing, null as null | { recall_count?: number });
|
|
639
|
+
record.recall_count = prev?.recall_count ?? 0;
|
|
619
640
|
}
|
|
620
641
|
await redis.set(key, JSON.stringify(record));
|
|
621
642
|
return `🌐 **Global lesson stored**: \`${topic}\`\n\n${lesson}\n\nRecallable from any project via \`global_recall(topic="${topic}")\`.`;
|
|
@@ -635,10 +656,12 @@ export async function handleTeamTool(
|
|
|
635
656
|
|
|
636
657
|
type GlobalLesson = { topic: string; lesson: string; severity?: string; ts: string; recall_count?: number };
|
|
637
658
|
let lessons: GlobalLesson[] = [];
|
|
638
|
-
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
659
|
+
if (keys.length > 0) {
|
|
660
|
+
const raws = await redis.mget(...keys);
|
|
661
|
+
for (const raw of raws) {
|
|
662
|
+
const l = safeJsonParse<GlobalLesson | null>(raw ?? null, null);
|
|
663
|
+
if (l) lessons.push(l);
|
|
664
|
+
}
|
|
642
665
|
}
|
|
643
666
|
|
|
644
667
|
if (topic) {
|
|
@@ -655,7 +678,8 @@ export async function handleTeamTool(
|
|
|
655
678
|
const k = `cachly:global:lesson:${l.topic}`;
|
|
656
679
|
const raw = await redis.get(k);
|
|
657
680
|
if (raw) {
|
|
658
|
-
const rec =
|
|
681
|
+
const rec = safeJsonParse(raw, null as null | { recall_count?: number });
|
|
682
|
+
if (!rec) continue;
|
|
659
683
|
rec.recall_count = (rec.recall_count ?? 0) + 1;
|
|
660
684
|
await redis.set(k, JSON.stringify(rec));
|
|
661
685
|
}
|