@cachly-dev/mcp-server 0.10.111 → 0.10.114
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 +28 -8
- package/dist/src/brain-watch-hook.d.ts +21 -0
- package/dist/src/brain-watch-hook.js +86 -0
- package/dist/src/brain-watch-hook.js.map +1 -0
- package/dist/src/handlers/brain.js +98 -2
- package/dist/src/handlers/brain.js.map +1 -1
- package/dist/src/handlers/cache.js +30 -2
- package/dist/src/handlers/cache.js.map +1 -1
- package/dist/src/handlers/fedbrain.js +34 -1
- package/dist/src/handlers/fedbrain.js.map +1 -1
- package/dist/src/index.js +4 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/tools.d.ts +63 -0
- package/dist/src/tools.js +57 -0
- package/dist/src/tools.js.map +1 -1
- package/glama.json +36 -0
- package/package.json +4 -3
- package/server.json +3 -3
package/README.md
CHANGED
|
@@ -101,6 +101,26 @@ Like what you see? Make it permanent in the next step.
|
|
|
101
101
|
|
|
102
102
|
---
|
|
103
103
|
|
|
104
|
+
## Brain-first — Semantic Cache as Proof-Point
|
|
105
|
+
|
|
106
|
+
cachly is not a semantic cache with a brain bolt-on. The Brain is the product. The
|
|
107
|
+
Semantic Cache is the **proof-point** — it shows ROI in dollars from day one, with zero
|
|
108
|
+
trust required. It opens the door. The Brain is why teams never leave.
|
|
109
|
+
|
|
110
|
+
| | Wedge — Land | Moat — Retain |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| **Feature** | Semantic Cache | AI Brain (Lessons, Recall, Team-Sharing) |
|
|
113
|
+
| **Value** | Measurable cost savings from day one | Compounding team intelligence |
|
|
114
|
+
| **Metric** | Cache-hit rate, $/month saved | Lessons retained, WoW trend, recall quality |
|
|
115
|
+
| **Analogy** | Datadog APM (surfaces the problem) | Stripe (becomes critical infrastructure) |
|
|
116
|
+
|
|
117
|
+
**The org-level advantage:** Brain lessons and cache hits are shared across the whole
|
|
118
|
+
team — one person's fix becomes every agent's reflex. Anthropic Projects Memory is
|
|
119
|
+
per-user and model-locked. cachly is team-wide and model-neutral. That's the structural
|
|
120
|
+
moat no first-party tool can build.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
104
124
|
## Setup — one command
|
|
105
125
|
|
|
106
126
|
```bash
|
|
@@ -229,7 +249,7 @@ causal_trace(problem="auth breaks after restart")
|
|
|
229
249
|
npx @cachly-dev/mcp-server@latest autopilot # One command — signs in, configures every editor, bootstraps from git
|
|
230
250
|
npx @cachly-dev/mcp-server@latest demo # Preview your Brain (no account needed)
|
|
231
251
|
npx @cachly-dev/mcp-server@latest bench # Recall quality vs flat-file memory (no auth required)
|
|
232
|
-
npx @cachly-dev/mcp-server@latest
|
|
252
|
+
npx @cachly-dev/mcp-server@latest autosetup # Interactive variant — pick editors yourself
|
|
233
253
|
npx @cachly-dev/mcp-server@latest health # Check token, API, editors, git hook
|
|
234
254
|
npx @cachly-dev/mcp-server@latest digest # Weekly Brain summary — shareable
|
|
235
255
|
npx @cachly-dev/mcp-server@latest share # Generate a shareable stats card + tweet
|
|
@@ -378,17 +398,17 @@ following. Manual snippets are in the **Manual Setup** section below.
|
|
|
378
398
|
| **Cline** | ✅ detected via VS Code | `.vscode/mcp.json` | — | Shares config with Copilot; restart VS Code |
|
|
379
399
|
| **Continue.dev** | ✅ detected via `.continue/` | `.continue/config.json` | — | Uses `modelContextProtocolServers` key |
|
|
380
400
|
| **Zed** | ✅ detected via `.zed/` | `.zed/settings.json` | — | Uses `context_servers` key |
|
|
381
|
-
| **Windsurf (global)** | `
|
|
382
|
-
| **Any other MCP client** | `
|
|
401
|
+
| **Windsurf (global)** | `autosetup --editor windsurf` | `~/.windsurf/mcp.json` | ✅ | Pass `--editor` to target global config |
|
|
402
|
+
| **Any other MCP client** | `autosetup --editor claude` | `.mcp.json` | — | Standard `mcpServers` stdio format |
|
|
383
403
|
|
|
384
404
|
**Which sign-in path each editor uses:**
|
|
385
405
|
|
|
386
406
|
| Scenario | Path |
|
|
387
407
|
|---|---|
|
|
388
|
-
| `
|
|
389
|
-
| `
|
|
408
|
+
| `autosetup` from a real terminal (TTY) | OAuth device-flow → browser click → API key saved automatically |
|
|
409
|
+
| `autosetup` from VSCode task / CI (non-TTY) | Auto-detects non-interactive, opens browser with step-by-step guide, prints `CACHLY_JWT=... autosetup` instruction |
|
|
390
410
|
| First tool call from Claude Code (no JWT yet) | Inline device-flow: MCP returns URL + code, browser opens automatically, next call proceeds |
|
|
391
|
-
| `CACHLY_JWT=cky_live_xxx npx ...
|
|
411
|
+
| `CACHLY_JWT=cky_live_xxx npx ... autosetup` | Skips auth step entirely, uses provided key |
|
|
392
412
|
|
|
393
413
|
> **Tip — fastest per-project setup from inside Claude Code:**
|
|
394
414
|
> ```
|
|
@@ -480,12 +500,12 @@ private backend instead of `api.cachly.dev`:
|
|
|
480
500
|
npx @cachly-dev/mcp-server@latest autopilot --api-url https://cachly.mycorp.internal
|
|
481
501
|
|
|
482
502
|
# Or non-interactively
|
|
483
|
-
npx @cachly-dev/mcp-server@latest
|
|
503
|
+
npx @cachly-dev/mcp-server@latest autosetup \
|
|
484
504
|
--instance-id <uuid> --api-key <cky_live_...> \
|
|
485
505
|
--api-url https://cachly.mycorp.internal
|
|
486
506
|
```
|
|
487
507
|
|
|
488
|
-
`
|
|
508
|
+
`autosetup` bakes `CACHLY_API_URL` into the editor config **only** when it differs
|
|
489
509
|
from the default cloud — so default installs stay clean, and self-hosted installs
|
|
490
510
|
keep talking to your backend on every editor launch. All data stays in your infra.
|
|
491
511
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Bumped whenever the hook script changes so installers can upgrade old hooks. */
|
|
2
|
+
export declare const BRAIN_WATCH_HOOK_VERSION = "v1";
|
|
3
|
+
export type BrainWatchHookResult = 'written' | 'upgraded' | 'appended' | 'unchanged' | 'skipped-no-git';
|
|
4
|
+
/**
|
|
5
|
+
* Idempotently install/upgrade the brain_watch post-commit hook in `projectDir`.
|
|
6
|
+
* Never throws — returns a status string the caller can report. Safe to run
|
|
7
|
+
* repeatedly; a second call with the same version returns 'unchanged'.
|
|
8
|
+
*/
|
|
9
|
+
export declare function installBrainWatchHook(projectDir: string, instanceId: string, apiKey?: string): Promise<{
|
|
10
|
+
result: BrainWatchHookResult;
|
|
11
|
+
hookPath: string;
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* Build the `.git/hooks/post-commit` shell script for brain_watch.
|
|
15
|
+
* Uses curl (not Node) so it works without npx. Runs in the background and
|
|
16
|
+
* always exits 0 — never blocks a commit.
|
|
17
|
+
*
|
|
18
|
+
* @param instanceId Cachly Brain instance to learn into.
|
|
19
|
+
* @param apiKey Optional cky_… key; if absent, the script reads $CACHLY_JWT from env.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildBrainWatchHook(instanceId: string, apiKey?: string): string;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Ambient git post-commit hook builder for Brain auto-learning.
|
|
2
|
+
//
|
|
3
|
+
// Installs a .git/hooks/post-commit script that POSTs the commit data
|
|
4
|
+
// directly to the cachly API via curl — no Node required at hook runtime,
|
|
5
|
+
// so it works even without npx or the MCP server installed locally.
|
|
6
|
+
//
|
|
7
|
+
// Safety contract:
|
|
8
|
+
// • exit 0 is ALWAYS the last line — the hook never blocks a commit.
|
|
9
|
+
// • All network calls run in the background (&) with 2>/dev/null.
|
|
10
|
+
// • Commit message / sha / files are never interpolated into JS source;
|
|
11
|
+
// they're passed as shell variables in the JSON body (curl --data-raw).
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
import { readFile, writeFile, chmod, mkdir } from 'node:fs/promises';
|
|
14
|
+
import { resolve } from 'node:path';
|
|
15
|
+
/** Bumped whenever the hook script changes so installers can upgrade old hooks. */
|
|
16
|
+
export const BRAIN_WATCH_HOOK_VERSION = 'v1';
|
|
17
|
+
/**
|
|
18
|
+
* Matches any previously-installed cachly brain_watch block (from its marker
|
|
19
|
+
* comment down to the trailing `exit 0`), optionally including a shebang.
|
|
20
|
+
* Used to upgrade old hooks in place rather than duplicating them.
|
|
21
|
+
*/
|
|
22
|
+
const BRAIN_WATCH_BLOCK_RE = /(?:#!\/bin\/sh\n)?# cachly brain_watch[\s\S]*?\nexit 0\n?/;
|
|
23
|
+
/**
|
|
24
|
+
* Idempotently install/upgrade the brain_watch post-commit hook in `projectDir`.
|
|
25
|
+
* Never throws — returns a status string the caller can report. Safe to run
|
|
26
|
+
* repeatedly; a second call with the same version returns 'unchanged'.
|
|
27
|
+
*/
|
|
28
|
+
export async function installBrainWatchHook(projectDir, instanceId, apiKey) {
|
|
29
|
+
if (!existsSync(resolve(projectDir, '.git'))) {
|
|
30
|
+
return { result: 'skipped-no-git', hookPath: '' };
|
|
31
|
+
}
|
|
32
|
+
const hookDir = resolve(projectDir, '.git', 'hooks');
|
|
33
|
+
const hookPath = resolve(hookDir, 'post-commit');
|
|
34
|
+
await mkdir(hookDir, { recursive: true });
|
|
35
|
+
const script = buildBrainWatchHook(instanceId, apiKey);
|
|
36
|
+
let existing = '';
|
|
37
|
+
try {
|
|
38
|
+
existing = await readFile(hookPath, 'utf-8');
|
|
39
|
+
}
|
|
40
|
+
catch { /* no existing hook */ }
|
|
41
|
+
if (!existing) {
|
|
42
|
+
await writeFile(hookPath, script + '\n', 'utf-8');
|
|
43
|
+
await chmod(hookPath, 0o755).catch(() => { });
|
|
44
|
+
return { result: 'written', hookPath };
|
|
45
|
+
}
|
|
46
|
+
if (existing.includes(`cachly brain_watch — Auto-Learn ${BRAIN_WATCH_HOOK_VERSION}`)) {
|
|
47
|
+
return { result: 'unchanged', hookPath };
|
|
48
|
+
}
|
|
49
|
+
if (BRAIN_WATCH_BLOCK_RE.test(existing)) {
|
|
50
|
+
const replaced = existing.replace(BRAIN_WATCH_BLOCK_RE, script);
|
|
51
|
+
await writeFile(hookPath, replaced.endsWith('\n') ? replaced : replaced + '\n', 'utf-8');
|
|
52
|
+
await chmod(hookPath, 0o755).catch(() => { });
|
|
53
|
+
return { result: 'upgraded', hookPath };
|
|
54
|
+
}
|
|
55
|
+
// Foreign hook with no brain_watch block → append ours.
|
|
56
|
+
await writeFile(hookPath, existing.trimEnd() + '\n\n' + script + '\n', 'utf-8');
|
|
57
|
+
await chmod(hookPath, 0o755).catch(() => { });
|
|
58
|
+
return { result: 'appended', hookPath };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build the `.git/hooks/post-commit` shell script for brain_watch.
|
|
62
|
+
* Uses curl (not Node) so it works without npx. Runs in the background and
|
|
63
|
+
* always exits 0 — never blocks a commit.
|
|
64
|
+
*
|
|
65
|
+
* @param instanceId Cachly Brain instance to learn into.
|
|
66
|
+
* @param apiKey Optional cky_… key; if absent, the script reads $CACHLY_JWT from env.
|
|
67
|
+
*/
|
|
68
|
+
export function buildBrainWatchHook(instanceId, apiKey) {
|
|
69
|
+
return [
|
|
70
|
+
`#!/bin/sh`,
|
|
71
|
+
`# cachly brain_watch — Auto-Learn ${BRAIN_WATCH_HOOK_VERSION}`,
|
|
72
|
+
`# Silently teaches your Brain after every commit — no manual brain_from_git needed.`,
|
|
73
|
+
...(apiKey ? [`CACHLY_JWT="${apiKey}"`] : []),
|
|
74
|
+
`_BW_SHA=$(git rev-parse HEAD 2>/dev/null || echo "")`,
|
|
75
|
+
`_BW_MSG=$(git log -1 --pretty=%B 2>/dev/null | head -1 | cut -c1-200)`,
|
|
76
|
+
`_BW_FILES=$(git diff-tree --no-commit-id -r --name-only HEAD 2>/dev/null | tr '\\n' '\\t' | sed 's/\\t$//')`,
|
|
77
|
+
`(curl -s -o /dev/null -m 8 \\`,
|
|
78
|
+
` -X POST "https://api.cachly.dev/api/v1/instances/${instanceId}/learn" \\`,
|
|
79
|
+
` -H "Authorization: Bearer \${CACHLY_JWT}" \\`,
|
|
80
|
+
` -H "Content-Type: application/json" \\`,
|
|
81
|
+
` --data-raw "{\\"message\\":\\"$(echo "$_BW_MSG" | sed 's/"/\\\\"/g')\\",\\"sha\\":\\"$_BW_SHA\\",\\"files\\":[$(echo "$_BW_FILES" | tr '\\t' '\\n' | grep -v '^$' | sed 's/.*/\\"&\\"/' | tr '\\n' ',' | sed 's/,$//')],\\"source\\":\\"brain_watch\\"}" \\`,
|
|
82
|
+
`) 2>/dev/null &`,
|
|
83
|
+
`exit 0`,
|
|
84
|
+
].join('\n');
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=brain-watch-hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brain-watch-hook.js","sourceRoot":"","sources":["../../src/brain-watch-hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,8 +7,28 @@ import { keywordSearch, tokenize, splitMultiQuery, levenshtein, indexVocab as _i
|
|
|
7
7
|
import { rerankByQuality } from '../rerank.js';
|
|
8
8
|
import { computeEmbedding, hasEmbedProvider } from '../embeddings.js';
|
|
9
9
|
// ── Changelog (shown once per version in session_start) ──────────────────────
|
|
10
|
-
const MCP_VERSION = '0.10.
|
|
10
|
+
const MCP_VERSION = '0.10.114';
|
|
11
11
|
const WHATS_NEW = {
|
|
12
|
+
'0.10.114': [
|
|
13
|
+
`📋 **\`brain_changelog\` — weekly knowledge digest in one call**`,
|
|
14
|
+
` 📅 Generates a grouped Markdown changelog of lessons learned in the last N days.`,
|
|
15
|
+
` 👥 Grouped by topic category, annotated with author, recall count, and confidence — paste directly into standup or Slack.`,
|
|
16
|
+
` ⚙️ Params: \`days\` (default 7), \`max_lessons\` (default 30), \`include_failures\` (default true).`,
|
|
17
|
+
` 📊 138 MCP tools`,
|
|
18
|
+
],
|
|
19
|
+
'0.10.113': [
|
|
20
|
+
`📈 **Week-over-week savings trend in \`cache_stats\`**`,
|
|
21
|
+
` 📅 \`cache_stats\` now shows **7-day hits** + **WoW trend %** (e.g. \`+23.0% vs prev week\`) so ROI momentum is visible, not just the cumulative total.`,
|
|
22
|
+
` ⬇️ Goes red (📉) on drops, green (📈) on growth — first week always shows +100% (new).`,
|
|
23
|
+
` 🔧 Backed by new \`CountHitsInWindow\` repo method + \`hits_last_7d\`, \`hits_prev_7d\`, \`week_over_week_pct\` fields in \`SavingsEstimate\`.`,
|
|
24
|
+
` 📊 137 MCP tools`,
|
|
25
|
+
],
|
|
26
|
+
'0.10.112': [
|
|
27
|
+
`⚙️ **Configurable cost_per_call_usd — accurate ROI for any LLM**`,
|
|
28
|
+
` 💡 New \`set_cost_per_call\` tool: set your real per-call cost (claude-opus-4.8 → $0.02, gpt-5.5 → $0.015, claude-sonnet-4.6 → $0.009, claude-haiku-4.5 → $0.001) so savings estimates in \`cache_stats\` reflect your actual bill.`,
|
|
29
|
+
` 🔧 New API: \`PUT /api/v1/instances/:id/cost-per-call\` — persisted per instance, used by \`cache_stats\`, org savings, and SSE analytics stream.`,
|
|
30
|
+
` 📊 137 MCP tools`,
|
|
31
|
+
],
|
|
12
32
|
'0.10.111': [
|
|
13
33
|
`🏢 **Org-wide ROI + GitLab CI — the CFO number, aggregated**`,
|
|
14
34
|
` 💰 \`cache_org_stats\` now shows **org-wide Tokenmaxxing ROI**: total savings summed across every instance in your org, monthly projection, top-5 instances by savings. One number for the whole team.`,
|
|
@@ -335,7 +355,7 @@ export const BRAIN_TOOL_NAMES = new Set([
|
|
|
335
355
|
'session_start', 'session_start_summary', 'session_end', 'session_ping', 'session_handoff', 'auto_learn_session',
|
|
336
356
|
'brain_who_knows', 'brain_file_map', 'team_expertise_map',
|
|
337
357
|
'skill_gaps', 'brain_coverage', 'brain_metrics', 'brain_service_map',
|
|
338
|
-
'brain_collab_pairs', 'brain_portability',
|
|
358
|
+
'brain_collab_pairs', 'brain_portability', 'brain_changelog',
|
|
339
359
|
]);
|
|
340
360
|
// ── Free-tier Teaser-Gate ──────────────────────────────────────────────────
|
|
341
361
|
// When a Free-tier Brain crosses its recall limit, smart_recall still delivers
|
|
@@ -3252,6 +3272,82 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
3252
3272
|
lines.push(`---`, ``, `### What travels with you`, ``, `| Data | Model-neutral? |`, `|------|---------------|`, `| Lessons (learn_from_attempts) | ✅ Full access in all clients |`, `| Memory Crystals (memory_crystalize) | ✅ Included in every session_start |`, `| Causal Knowledge Graph (brain_predict) | ✅ Same graph, same predictions |`, `| Team lessons (team_learn, team_confirm) | ✅ Team is per-Brain, not per-model |`, `| Domain Brain Marketplace installs | ✅ Installed once, available everywhere |`, `| Session handoffs (session_handoff) | ✅ Cross-client continuity |`, ``, `---`, ``, `### One-command setup for any new client`, ``, '```bash', `npx @cachly-dev/mcp-server@latest autopilot`, '```', ``, `Autopilot detects all installed editors and writes the config for each — with your \`${instance_id}\` Brain ID pre-filled.`, ``, `---`, `_Docs: cachly.dev/docs/model-neutral • Your Brain, Any Model_`);
|
|
3253
3273
|
return lines.join('\n');
|
|
3254
3274
|
}
|
|
3275
|
+
// ── brain_changelog ───────────────────────────────────────────────────────
|
|
3276
|
+
// Generates a human-readable Markdown changelog of recent lessons.
|
|
3277
|
+
// Useful for weekly standups, sprint reviews, or async team updates.
|
|
3278
|
+
case 'brain_changelog': {
|
|
3279
|
+
const { instance_id, days = 7, max_lessons = 30, include_failures = true, } = args;
|
|
3280
|
+
const redis = await getConnection(instance_id);
|
|
3281
|
+
const cutoff = Date.now() - Number(days) * 86_400_000;
|
|
3282
|
+
// Scan all lessons and collect recent ones.
|
|
3283
|
+
const keys = await scanKeys(redis, 'cachly:lesson:best:*', { max: 4000, timeoutMs: 4000 });
|
|
3284
|
+
const recent = [];
|
|
3285
|
+
for (const k of keys) {
|
|
3286
|
+
const raw = await redis.get(k).catch(() => null);
|
|
3287
|
+
if (!raw)
|
|
3288
|
+
continue;
|
|
3289
|
+
const lesson = safeJsonParse(raw, {});
|
|
3290
|
+
const ts = lesson.ts ? new Date(lesson.ts).getTime() : 0;
|
|
3291
|
+
if (ts < cutoff)
|
|
3292
|
+
continue;
|
|
3293
|
+
if (!include_failures && lesson.outcome === 'failure')
|
|
3294
|
+
continue;
|
|
3295
|
+
const topic = k.replace('cachly:lesson:best:', '');
|
|
3296
|
+
recent.push({ ...lesson, topic });
|
|
3297
|
+
}
|
|
3298
|
+
// Sort by timestamp desc, cap at max_lessons.
|
|
3299
|
+
recent.sort((a, b) => {
|
|
3300
|
+
const ta = a.ts ? new Date(a.ts).getTime() : 0;
|
|
3301
|
+
const tb = b.ts ? new Date(b.ts).getTime() : 0;
|
|
3302
|
+
return tb - ta;
|
|
3303
|
+
});
|
|
3304
|
+
const lessons = recent.slice(0, Number(max_lessons));
|
|
3305
|
+
if (lessons.length === 0) {
|
|
3306
|
+
return [
|
|
3307
|
+
`## 📋 Brain Changelog — last ${days} day${Number(days) !== 1 ? 's' : ''}`,
|
|
3308
|
+
``,
|
|
3309
|
+
`_No new lessons learned in this window. Your Brain already knows everything it knows — add new lessons with \`learn_from_attempts\`._`,
|
|
3310
|
+
].join('\n');
|
|
3311
|
+
}
|
|
3312
|
+
// Group by category (topic prefix before first ':').
|
|
3313
|
+
const groups = new Map();
|
|
3314
|
+
for (const l of lessons) {
|
|
3315
|
+
const cat = l.topic.includes(':') ? l.topic.split(':')[0] : 'general';
|
|
3316
|
+
const arr = groups.get(cat) ?? [];
|
|
3317
|
+
arr.push(l);
|
|
3318
|
+
groups.set(cat, arr);
|
|
3319
|
+
}
|
|
3320
|
+
const severityEmoji = (s) => s === 'critical' ? '🚨' : s === 'major' ? '⚠️' : s === 'minor' ? '💡' : '✨';
|
|
3321
|
+
const outcomeEmoji = (o) => o === 'failure' ? '❌' : o === 'partial' ? '🔶' : '✅';
|
|
3322
|
+
const dateStr = new Date().toISOString().slice(0, 10);
|
|
3323
|
+
const lines = [
|
|
3324
|
+
`## 📋 Brain Changelog — ${dateStr} (last ${days} day${Number(days) !== 1 ? 's' : ''})`,
|
|
3325
|
+
``,
|
|
3326
|
+
`_${lessons.length} lesson${lessons.length !== 1 ? 's' : ''} learned across ${groups.size} topic area${groups.size !== 1 ? 's' : ''}_`,
|
|
3327
|
+
``,
|
|
3328
|
+
];
|
|
3329
|
+
for (const [cat, catLessons] of Array.from(groups.entries()).sort((a, b) => b[1].length - a[1].length)) {
|
|
3330
|
+
lines.push(`### ${cat}`);
|
|
3331
|
+
lines.push('');
|
|
3332
|
+
for (const l of catLessons) {
|
|
3333
|
+
const icon = l.outcome === 'failure' ? outcomeEmoji(l.outcome) : severityEmoji(l.severity);
|
|
3334
|
+
const body = l.outcome === 'failure' ? l.what_failed : l.what_worked;
|
|
3335
|
+
const meta = [];
|
|
3336
|
+
if (l.author)
|
|
3337
|
+
meta.push(`by **${l.author}**`);
|
|
3338
|
+
if (l.recall_count)
|
|
3339
|
+
meta.push(`${l.recall_count} recall${l.recall_count !== 1 ? 's' : ''}`);
|
|
3340
|
+
if (l.confidence !== undefined)
|
|
3341
|
+
meta.push(`${Math.round(l.confidence * 100)}% confidence`);
|
|
3342
|
+
lines.push(`- ${icon} **${l.topic}**${body ? ` — ${body}` : ''}`);
|
|
3343
|
+
if (meta.length)
|
|
3344
|
+
lines.push(` _${meta.join(' · ')}_`);
|
|
3345
|
+
}
|
|
3346
|
+
lines.push('');
|
|
3347
|
+
}
|
|
3348
|
+
lines.push('---', `_Generated from Brain \`${instance_id}\` · Share this in your standup or Slack_`);
|
|
3349
|
+
return lines.join('\n');
|
|
3350
|
+
}
|
|
3255
3351
|
// ── sync_file_changes ─────────────────────────────────────────────────────
|
|
3256
3352
|
default:
|
|
3257
3353
|
return null;
|