@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.
Files changed (44) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +50 -0
  2. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
  4. package/.github/workflows/publish.yml +72 -0
  5. package/CHANGELOG.md +28 -0
  6. package/LAUNCH_TEXTS.md +182 -0
  7. package/LICENSE +202 -0
  8. package/README.md +113 -112
  9. package/dist/__tests__/auth.test.d.ts +1 -0
  10. package/dist/__tests__/auth.test.js +107 -0
  11. package/dist/__tests__/auth.test.js.map +1 -0
  12. package/dist/handlers/brain.js +7 -67
  13. package/dist/handlers/brain.js.map +1 -1
  14. package/dist/handlers/fedbrain.js +93 -44
  15. package/dist/handlers/fedbrain.js.map +1 -1
  16. package/dist/handlers/roadmap.js +8 -2
  17. package/dist/handlers/roadmap.js.map +1 -1
  18. package/dist/handlers/syndicate.js +15 -7
  19. package/dist/handlers/syndicate.js.map +1 -1
  20. package/dist/handlers/team.js +62 -50
  21. package/dist/handlers/team.js.map +1 -1
  22. package/dist/index.js +555 -93
  23. package/dist/index.js.map +1 -1
  24. package/dist/tools.d.ts +3 -54
  25. package/dist/tools.js +8 -62
  26. package/dist/tools.js.map +1 -1
  27. package/glama.json +29 -0
  28. package/llms.txt +155 -0
  29. package/package.json +29 -13
  30. package/scripts/postinstall.js +12 -12
  31. package/server.json +3 -9
  32. package/smithery.yaml +6 -8
  33. package/src/__tests__/auth.test.ts +147 -0
  34. package/src/handlers/brain.ts +7 -64
  35. package/src/handlers/fedbrain.ts +88 -38
  36. package/src/handlers/roadmap.ts +7 -2
  37. package/src/handlers/syndicate.ts +13 -7
  38. package/src/handlers/team.ts +57 -33
  39. package/src/index.ts +599 -106
  40. package/src/tools.ts +8 -66
  41. package/dist/__tests__/brain-predict-failures.test.d.ts +0 -17
  42. package/dist/__tests__/brain-predict-failures.test.js +0 -339
  43. package/dist/__tests__/brain-predict-failures.test.js.map +0 -1
  44. package/src/__tests__/brain-predict-failures.test.ts +0 -430
package/src/tools.ts CHANGED
@@ -3,18 +3,6 @@
3
3
  // ── Tools ─────────────────────────────────────────────────────────────────────
4
4
 
5
5
  const TOOLS = [
6
- // ── Auth / Onboarding ────────────────────────────────────────────────────
7
- {
8
- name: 'sign_in',
9
- description:
10
- 'Sign in to cachly AI Brain. Call this FIRST if no CACHLY_JWT is configured. ' +
11
- 'Starts an OAuth 2.0 Device Flow: you visit a short URL in your browser, approve in one click, ' +
12
- 'and the brain is ready instantly — no copy-paste, no dashboard required. ' +
13
- 'After sign-in, a free Brain instance is auto-provisioned if you don\'t have one yet. ' +
14
- 'Once authenticated, all 63 brain tools become available. ' +
15
- 'IMPORTANT: AI assistants should call this tool proactively at session start when CACHLY_JWT is not set.',
16
- inputSchema: { type: 'object', properties: {}, required: [] },
17
- },
18
6
  // ── Instance Management ──────────────────────────────────────────────────
19
7
  {
20
8
  name: 'list_instances',
@@ -449,10 +437,11 @@ const TOOLS = [
449
437
  {
450
438
  name: 'get_api_status',
451
439
  description:
452
- 'Check the cachly API health and your authentication status. ' +
453
- 'Returns whether the JWT is valid, your user ID (sub claim), ' +
454
- 'token expiry, and the auth provider (keycloak). ' +
455
- 'Use this to debug connection issues or verify your CACHLY_JWT is correct.',
440
+ 'Full diagnostic for your cachly Brain — call this FIRST whenever anything is not working. ' +
441
+ 'Returns: API reachability, JWT validity + expiry, your user ID, ' +
442
+ 'all Brain instances with live status (🟢 running / 🟡 provisioning / 🔴 stopped), ' +
443
+ 'Redis ping on the active connection, and actionable fix steps for every issue found. ' +
444
+ 'Workflow: run get_api_status → read the issue it flags → fix it → retry your tool.',
456
445
  inputSchema: { type: 'object', properties: {}, required: [] },
457
446
  },
458
447
  // ── Thinking/Context Cache (for AI assistants) ────────────────────────────
@@ -1707,7 +1696,8 @@ const TOOLS = [
1707
1696
  description:
1708
1697
  'Bootstrap brain lessons from git history. Parses commit messages and infers fix/feature/refactor ' +
1709
1698
  'lessons automatically. Great for onboarding an existing codebase — run once and the brain instantly ' +
1710
- 'knows your team\'s accumulated patterns. Supports limit and branch options.',
1699
+ 'knows your team\'s accumulated patterns. Incremental by default: only processes new commits since the ' +
1700
+ 'last run, so repeated calls are fast. Emits progress updates to stderr during long scans.',
1711
1701
  inputSchema: {
1712
1702
  type: 'object',
1713
1703
  properties: {
@@ -1716,59 +1706,11 @@ const TOOLS = [
1716
1706
  limit: { type: 'number', description: 'Max commits to process (default: 100, max: 500)' },
1717
1707
  branch: { type: 'string', description: 'Git branch to parse (default: current branch / HEAD)' },
1718
1708
  since: { type: 'string', description: 'Only commits after this date, e.g. "2024-01-01" (optional)' },
1709
+ incremental: { type: 'boolean', description: 'Only process commits since last run (default: true). Set false to reprocess all.' },
1719
1710
  },
1720
1711
  required: ['instance_id'],
1721
1712
  },
1722
1713
  },
1723
- // ── Brain Stats + Pinning ────────────────────────────────────────────────
1724
- {
1725
- name: 'brain_stats',
1726
- description:
1727
- 'At-a-glance health dashboard for your AI Brain. Returns lesson count, context entries, ' +
1728
- 'roadmap items, active streak, last session summary, pinned lessons, top 5 topics by frequency, ' +
1729
- 'and crystal status — all in one call. ' +
1730
- 'Use this after `session_start` when you want a quick state-of-the-brain view. ' +
1731
- 'Read-only — no side effects.',
1732
- inputSchema: {
1733
- type: 'object',
1734
- properties: {
1735
- instance_id: { type: 'string', description: 'UUID of the cache instance' },
1736
- },
1737
- required: ['instance_id'],
1738
- },
1739
- },
1740
- {
1741
- name: 'pin_lesson',
1742
- description:
1743
- 'Pin a lesson to ALWAYS appear in session_start, regardless of age or confidence decay. ' +
1744
- 'Use for critical lessons that must never be forgotten: platform quirks, security rules, ' +
1745
- 'non-obvious gotchas (e.g. "bash:macos-lowercase: use tr not ${var,,}"). ' +
1746
- 'Pinned lessons are shown at the top of every session briefing with a 📌 icon. ' +
1747
- 'Use unpin_lesson to remove a pin. Idempotent — pinning the same topic twice is safe.',
1748
- inputSchema: {
1749
- type: 'object',
1750
- properties: {
1751
- instance_id: { type: 'string', description: 'UUID of the cache instance' },
1752
- topic: { type: 'string', description: 'Topic slug to pin (must already exist via learn_from_attempts)' },
1753
- },
1754
- required: ['instance_id', 'topic'],
1755
- },
1756
- },
1757
- {
1758
- name: 'unpin_lesson',
1759
- description:
1760
- 'Remove a pin from a lesson so it follows normal recency/confidence decay in session_start. ' +
1761
- 'The lesson data is preserved — only the pin marker is removed. ' +
1762
- 'Use `list_pinned` (via brain_stats) to see all currently pinned topics.',
1763
- inputSchema: {
1764
- type: 'object',
1765
- properties: {
1766
- instance_id: { type: 'string', description: 'UUID of the cache instance' },
1767
- topic: { type: 'string', description: 'Topic slug to unpin' },
1768
- },
1769
- required: ['instance_id', 'topic'],
1770
- },
1771
- },
1772
1714
  {
1773
1715
  name: 'brain_predict_failures',
1774
1716
  description:
@@ -1,17 +0,0 @@
1
- /**
2
- * Unit tests for the brain_predict_failures handler.
3
- *
4
- * Tests the pure scoring and ranking logic without real Redis:
5
- * - empty brain → "no known failure patterns" message
6
- * - lesson-based predictions: failure-outcome lessons matching context are surfaced
7
- * - deduplication: same failure text appears only once
8
- * - ranking: higher-probability failures come first
9
- * - format=brief: no fix line; format=detailed: fix + lesson lines included
10
- * - high-risk badge: ≥60% probability triggers the ⚠️ warning line
11
- * - no false positives: unrelated lessons are not returned
12
- * - severity boost: critical lessons rank above major ones with equal base score
13
- * - top_k limit: at most top_k results returned
14
- *
15
- * Run: npx vitest run src/__tests__/brain-predict-failures.test.ts
16
- */
17
- export {};
@@ -1,339 +0,0 @@
1
- /**
2
- * Unit tests for the brain_predict_failures handler.
3
- *
4
- * Tests the pure scoring and ranking logic without real Redis:
5
- * - empty brain → "no known failure patterns" message
6
- * - lesson-based predictions: failure-outcome lessons matching context are surfaced
7
- * - deduplication: same failure text appears only once
8
- * - ranking: higher-probability failures come first
9
- * - format=brief: no fix line; format=detailed: fix + lesson lines included
10
- * - high-risk badge: ≥60% probability triggers the ⚠️ warning line
11
- * - no false positives: unrelated lessons are not returned
12
- * - severity boost: critical lessons rank above major ones with equal base score
13
- * - top_k limit: at most top_k results returned
14
- *
15
- * Run: npx vitest run src/__tests__/brain-predict-failures.test.ts
16
- */
17
- import { describe, it, expect } from 'vitest';
18
- import { EventEmitter } from 'node:events';
19
- // ── Minimal MockRedis for brain_predict_failures ───────────────────────────────
20
- // The handler uses: get, set, smembers, scanStream (for cachly:lesson:best:* and cachly:ckg:node:*)
21
- class MockRedis {
22
- store = new Map();
23
- sets = new Map();
24
- async get(key) {
25
- return this.store.get(key) ?? null;
26
- }
27
- async set(key, value) {
28
- this.store.set(key, value);
29
- return 'OK';
30
- }
31
- async smembers(key) {
32
- return [...(this.sets.get(key) ?? [])];
33
- }
34
- /** scanStream: returns all matching keys synchronously via setImmediate */
35
- scanStream(opts) {
36
- const emitter = new EventEmitter();
37
- const pattern = opts.match.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
38
- const regex = new RegExp(`^${pattern}$`);
39
- const matches = [...this.store.keys()].filter(k => regex.test(k));
40
- setImmediate(() => {
41
- emitter.emit('data', matches);
42
- emitter.emit('end');
43
- });
44
- return emitter;
45
- }
46
- /** Seed a failure lesson */
47
- seedLesson(topic, opts) {
48
- const lesson = {
49
- topic,
50
- outcome: opts.outcome ?? 'failure',
51
- what_failed: opts.what_failed ?? `${topic} failed`,
52
- what_worked: opts.what_worked ?? `fix for ${topic}`,
53
- confidence: opts.confidence ?? 0.8,
54
- severity: opts.severity ?? 'major',
55
- };
56
- this.store.set(`cachly:lesson:best:${topic}`, JSON.stringify(lesson));
57
- }
58
- }
59
- // ── Stub getConnection ─────────────────────────────────────────────────────────
60
- // handleFedbrainTool calls getConnection(instance_id) internally.
61
- // We patch the module via vi.mock at the top of the file.
62
- import { vi } from 'vitest';
63
- // We need to mock getConnection inside fedbrain.ts.
64
- // Since fedbrain imports from a local module, we use vi.mock.
65
- vi.mock('../handlers/brain.js', async (importOriginal) => {
66
- const orig = await importOriginal();
67
- return { ...orig };
68
- });
69
- // Helper: call brain_predict_failures with a pre-seeded redis mock
70
- async function callPredict(redis, args) {
71
- // We inject redis by temporarily replacing the getConnection function.
72
- // The fedbrain handler calls `getConnection(instance_id)` — we bypass this
73
- // by monkey-patching the module cache in this test environment.
74
- // Since ESM mocking is limited, we use a simpler approach:
75
- // invoke handleFedbrainTool directly with a tiny wrapper that provides our mock redis.
76
- // Direct approach: since handleFedbrainTool is a plain async function that calls
77
- // getConnection, and getConnection is imported inside the function,
78
- // we extract and test the pure scoring logic by seeding Redis and
79
- // verifying the output format.
80
- // For ESM-compatible testing without complex mocking, we call the handler
81
- // with a real (but empty) instance_id and intercept at the Redis level
82
- // by passing through the mock. However, handleFedbrainTool only accepts
83
- // instance_id as a string and calls getConnection internally.
84
- //
85
- // Approach: We test the output CONTRACT by examining what the function
86
- // would produce given specific Redis state, using a white-box input approach.
87
- // The tests below verify scoring logic by testing helper functions directly.
88
- throw new Error('Use testPredictLogic instead');
89
- }
90
- /** Mirror of the lesson-matching logic in brain_predict_failures */
91
- function scoreLesson(lesson, ctxTokens) {
92
- if (lesson.outcome !== 'failure' && lesson.outcome !== 'partial')
93
- return null;
94
- if (!lesson.what_failed)
95
- return null;
96
- const haystack = `${lesson.topic} ${lesson.what_failed}`.toLowerCase();
97
- const matchScore = ctxTokens.filter(t => haystack.includes(t)).length / Math.max(1, ctxTokens.length);
98
- if (matchScore < 0.15)
99
- return null;
100
- const sevBoost = lesson.severity === 'critical' ? 0.15 : lesson.severity === 'major' ? 0.05 : 0;
101
- return {
102
- concept: lesson.topic,
103
- failure: lesson.what_failed.slice(0, 80),
104
- probability: Math.min(0.97, (lesson.confidence ?? 0.5) * matchScore * 1.5 + sevBoost),
105
- fix: lesson.what_worked?.slice(0, 120),
106
- topic: lesson.topic,
107
- source: 'lesson',
108
- };
109
- }
110
- /** Mirror of the deduplication + ranking pipeline */
111
- function rankFailures(failures, topK) {
112
- const seen = new Set();
113
- return failures
114
- .filter(f => {
115
- const k = f.failure.slice(0, 40);
116
- if (seen.has(k))
117
- return false;
118
- seen.add(k);
119
- return true;
120
- })
121
- .sort((a, b) => b.probability - a.probability)
122
- .slice(0, topK);
123
- }
124
- /** Tokenizer mirror */
125
- function tokenize(ctx) {
126
- return ctx.toLowerCase().replace(/[^a-z0-9\s\-_:.]/g, ' ').split(/\s+/).filter(t => t.length > 2);
127
- }
128
- // ── Tests ─────────────────────────────────────────────────────────────────────
129
- describe('brain_predict_failures — scoring logic', () => {
130
- // ── scoreLesson ─────────────────────────────────────────────────────────────
131
- describe('scoreLesson', () => {
132
- it('returns null for success-outcome lessons', () => {
133
- const result = scoreLesson({ topic: 'deploy:api', outcome: 'success', what_failed: 'container crash', what_worked: 'fix' }, ['deploy', 'api']);
134
- expect(result).toBeNull();
135
- });
136
- it('returns null for lessons with no what_failed field', () => {
137
- const result = scoreLesson({ topic: 'deploy:api', outcome: 'failure' }, ['deploy', 'api']);
138
- expect(result).toBeNull();
139
- });
140
- it('returns null when context tokens do not match (matchScore < 0.15)', () => {
141
- const result = scoreLesson({ topic: 'deploy:api', outcome: 'failure', what_failed: 'container crash on startup' }, ['kubernetes', 'namespace', 'terminating']);
142
- expect(result).toBeNull();
143
- });
144
- it('returns a prediction when topic matches context', () => {
145
- const result = scoreLesson({ topic: 'deploy:api', outcome: 'failure', what_failed: 'deploy failed due to OOM', what_worked: 'add memory limit', confidence: 0.8 }, tokenize('deploy api memory'));
146
- expect(result).not.toBeNull();
147
- expect(result.source).toBe('lesson');
148
- expect(result.topic).toBe('deploy:api');
149
- expect(result.fix).toBe('add memory limit');
150
- });
151
- it('partial-outcome lessons are also surfaced', () => {
152
- const result = scoreLesson({ topic: 'fix:keycloak', outcome: 'partial', what_failed: 'keycloak jwks timeout', what_worked: 'retry 3min' }, tokenize('keycloak jwks rotation'));
153
- expect(result).not.toBeNull();
154
- });
155
- it('critical severity adds 0.15 probability boost', () => {
156
- const major = scoreLesson({ topic: 'fix:redis', outcome: 'failure', what_failed: 'redis connection timeout', confidence: 0.5, severity: 'major' }, tokenize('redis connection'));
157
- const critical = scoreLesson({ topic: 'fix:redis', outcome: 'failure', what_failed: 'redis connection timeout', confidence: 0.5, severity: 'critical' }, tokenize('redis connection'));
158
- expect(critical.probability).toBeGreaterThan(major.probability);
159
- expect(critical.probability - major.probability).toBeCloseTo(0.10, 1); // 0.15-0.05=0.10
160
- });
161
- it('probability is capped at 0.97', () => {
162
- const result = scoreLesson({ topic: 'fix:redis', outcome: 'failure', what_failed: 'redis', confidence: 0.99, severity: 'critical' }, tokenize('redis'));
163
- expect(result.probability).toBeLessThanOrEqual(0.97);
164
- });
165
- it('truncates long what_failed to 80 chars', () => {
166
- const longFailed = 'a'.repeat(200);
167
- const result = scoreLesson({ topic: 'fix:thing', outcome: 'failure', what_failed: longFailed }, tokenize('fix thing aaa'));
168
- if (result) {
169
- expect(result.failure.length).toBeLessThanOrEqual(80);
170
- }
171
- });
172
- });
173
- // ── rankFailures ────────────────────────────────────────────────────────────
174
- describe('rankFailures', () => {
175
- it('returns at most top_k items', () => {
176
- const preds = Array.from({ length: 10 }, (_, i) => ({
177
- concept: `fix:item${i}`,
178
- failure: `failure ${i}`,
179
- probability: Math.random(),
180
- source: 'lesson',
181
- }));
182
- const ranked = rankFailures(preds, 3);
183
- expect(ranked).toHaveLength(3);
184
- });
185
- it('sorts by probability descending', () => {
186
- const preds = [
187
- { concept: 'a', failure: 'low prob', probability: 0.3, source: 'lesson' },
188
- { concept: 'b', failure: 'high prob', probability: 0.9, source: 'lesson' },
189
- { concept: 'c', failure: 'mid prob', probability: 0.6, source: 'lesson' },
190
- ];
191
- const ranked = rankFailures(preds, 5);
192
- expect(ranked[0].failure).toBe('high prob');
193
- expect(ranked[1].failure).toBe('mid prob');
194
- expect(ranked[2].failure).toBe('low prob');
195
- });
196
- it('deduplicates by first 40 chars of failure text', () => {
197
- const failure = 'container out of memory on startup crash';
198
- const preds = [
199
- { concept: 'a', failure, probability: 0.8, source: 'lesson' },
200
- { concept: 'b', failure, probability: 0.7, source: 'ckg' }, // duplicate
201
- ];
202
- const ranked = rankFailures(preds, 5);
203
- expect(ranked).toHaveLength(1);
204
- expect(ranked[0].probability).toBe(0.8); // first one wins (already sorted before dedup)
205
- });
206
- it('empty input returns empty array', () => {
207
- expect(rankFailures([], 5)).toHaveLength(0);
208
- });
209
- });
210
- // ── tokenize ────────────────────────────────────────────────────────────────
211
- describe('tokenize', () => {
212
- it('lowercases and splits on whitespace', () => {
213
- const tokens = tokenize('Upgrading Keycloak 21→24');
214
- expect(tokens).toContain('upgrading');
215
- expect(tokens).toContain('keycloak');
216
- });
217
- it('filters tokens shorter than 3 chars', () => {
218
- const tokens = tokenize('go to db');
219
- expect(tokens).not.toContain('go');
220
- expect(tokens).not.toContain('to');
221
- expect(tokens).not.toContain('db');
222
- });
223
- it('keeps hyphenated words intact', () => {
224
- const tokens = tokenize('docker-compose up');
225
- expect(tokens.some(t => t.includes('docker'))).toBe(true);
226
- });
227
- });
228
- // ── output format validation ─────────────────────────────────────────────────
229
- describe('output format', () => {
230
- it('high-risk failures (≥60%) trigger the ⚠️ warning line', () => {
231
- const highRisk = [
232
- { concept: 'deploy:api', failure: 'container crash', probability: 0.75, fix: 'add restart policy', topic: 'deploy:api', source: 'lesson' },
233
- ];
234
- const lowRisk = [
235
- { concept: 'deploy:web', failure: 'build timeout', probability: 0.3, source: 'lesson' },
236
- ];
237
- // Simulate the output lines the handler would produce
238
- const buildLines = (ranked, fmt) => {
239
- const lines = [];
240
- for (let i = 0; i < ranked.length; i++) {
241
- const f = ranked[i];
242
- const pct = Math.round(f.probability * 100);
243
- const bar = '█'.repeat(Math.round(pct / 10)) + '░'.repeat(10 - Math.round(pct / 10));
244
- const icon = pct >= 70 ? '🔴' : pct >= 40 ? '🟡' : '🟢';
245
- lines.push(`${icon} **${i + 1}. ${f.failure}**`);
246
- lines.push(` Probability: ${bar} **${pct}%** (${f.source === 'ckg' ? 'CKG causal edge' : 'lesson history'})`);
247
- if (fmt === 'detailed' && f.fix)
248
- lines.push(` ✅ Pre-loaded fix: _${f.fix}_`);
249
- if (fmt === 'detailed' && f.topic)
250
- lines.push(` 📚 Lesson: \`${f.topic}\``);
251
- }
252
- const highRiskCount = ranked.filter(f => f.probability >= 0.6).length;
253
- if (highRiskCount > 0) {
254
- lines.push(`⚠️ **${highRiskCount} high-risk failure${highRiskCount > 1 ? 's' : ''} detected**`);
255
- }
256
- else {
257
- lines.push(`✅ No high-risk failures detected.`);
258
- }
259
- return lines.join('\n');
260
- };
261
- expect(buildLines(highRisk, 'detailed')).toContain('⚠️');
262
- expect(buildLines(lowRisk, 'detailed')).toContain('✅ No high-risk');
263
- });
264
- it('detailed format includes fix and lesson lines', () => {
265
- const ranked = [
266
- { concept: 'deploy:api', failure: 'container crash', probability: 0.75, fix: 'add restart policy', topic: 'deploy:api', source: 'lesson' },
267
- ];
268
- const lines = [];
269
- const f = ranked[0];
270
- if (f.fix)
271
- lines.push(` ✅ Pre-loaded fix: _${f.fix}_`);
272
- if (f.topic)
273
- lines.push(` 📚 Lesson: \`${f.topic}\``);
274
- expect(lines.join('\n')).toContain('Pre-loaded fix');
275
- expect(lines.join('\n')).toContain('deploy:api');
276
- });
277
- it('brief format omits fix and lesson lines', () => {
278
- // In brief mode, we simply skip adding the fix/topic lines
279
- const ranked = [
280
- { concept: 'deploy:api', failure: 'container crash', probability: 0.75, fix: 'add restart policy', topic: 'deploy:api', source: 'lesson' },
281
- ];
282
- const lines = [];
283
- const fmt = 'brief';
284
- const f = ranked[0];
285
- if (fmt === 'detailed' && f.fix)
286
- lines.push(` ✅ Pre-loaded fix: _${f.fix}_`);
287
- if (fmt === 'detailed' && f.topic)
288
- lines.push(` 📚 Lesson: \`${f.topic}\``);
289
- expect(lines).toHaveLength(0); // no fix or lesson lines in brief mode
290
- });
291
- it('🔴 icon for ≥70% probability, 🟡 for ≥40%, 🟢 for <40%', () => {
292
- const iconFor = (probability) => {
293
- const pct = Math.round(probability * 100);
294
- return pct >= 70 ? '🔴' : pct >= 40 ? '🟡' : '🟢';
295
- };
296
- expect(iconFor(0.75)).toBe('🔴');
297
- expect(iconFor(0.50)).toBe('🟡');
298
- expect(iconFor(0.25)).toBe('🟢');
299
- expect(iconFor(0.70)).toBe('🔴'); // boundary
300
- expect(iconFor(0.40)).toBe('🟡'); // boundary
301
- });
302
- });
303
- // ── integration: realistic scenario ─────────────────────────────────────────
304
- describe('realistic scenario: keycloak upgrade', () => {
305
- const ctx = 'upgrading Keycloak from 21 to 24';
306
- const tokens = tokenize(ctx);
307
- const lessons = [
308
- // Both lessons have equal token overlap ('keycloak', 'upgrading', 'from').
309
- // The only difference is severity: critical vs major.
310
- // Critical (+0.15 boost) must rank above major (+0.05 boost) when confidence is equal.
311
- { topic: 'fix:keycloak-jwks', outcome: 'failure', what_failed: 'keycloak authentication fails after upgrading from version 21', what_worked: 'flush realm cache and restart', confidence: 0.7, severity: 'critical' },
312
- { topic: 'fix:keycloak-db-migration', outcome: 'failure', what_failed: 'keycloak database migration fails after upgrading from version 21', what_worked: 'run liquibase migrate manually', confidence: 0.7, severity: 'major' },
313
- { topic: 'fix:redis-oom', outcome: 'failure', what_failed: 'redis out of memory', what_worked: 'set maxmemory-policy allkeys-lru', confidence: 0.9, severity: 'critical' },
314
- { topic: 'deploy:web', outcome: 'success', what_worked: 'npm run build && docker push' },
315
- ];
316
- it('returns keycloak-related failures, not unrelated redis/web lessons', () => {
317
- const results = lessons
318
- .map(l => scoreLesson(l, tokens))
319
- .filter((r) => r !== null);
320
- const topics = results.map(r => r.topic);
321
- expect(topics).toContain('fix:keycloak-jwks');
322
- expect(topics).toContain('fix:keycloak-db-migration');
323
- // redis-oom has no token overlap → should be filtered
324
- expect(topics).not.toContain('fix:redis-oom');
325
- // success lesson → always null
326
- expect(topics).not.toContain('deploy:web');
327
- });
328
- it('critical keycloak-jwks ranks above major db-migration when confidence is equal', () => {
329
- const results = lessons
330
- .map(l => scoreLesson(l, tokens))
331
- .filter((r) => r !== null);
332
- const ranked = rankFailures(results, 5);
333
- // Both lessons have the same confidence=0.7 and equal token overlap,
334
- // so the critical severity boost (+0.15) must put jwks above major (+0.05).
335
- expect(ranked[0].topic).toBe('fix:keycloak-jwks');
336
- });
337
- });
338
- });
339
- //# sourceMappingURL=brain-predict-failures.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"brain-predict-failures.test.js","sourceRoot":"","sources":["../../src/__tests__/brain-predict-failures.test.ts"],"names":[],"mappings":"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"}