@cachly-dev/mcp-server 0.5.39 โ†’ 0.5.41

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 CHANGED
@@ -1,11 +1,11 @@
1
- # ๐Ÿง  cachly AI Brain โ€” MCP Server
1
+ # cachly โ€” Give Your AI a Permanent Brain
2
2
 
3
- > **Persistent memory for Claude Code, Cursor, GitHub Copilot & Windsurf.**
4
- > Your AI remembers every lesson, every fix, every architecture decision โ€” forever.
3
+ > **Claude forgets everything when the session ends.**
4
+ > cachly doesn't. Every fix, every lesson, every architecture decision โ€” loaded in 2 seconds at the start of every session. Forever.
5
5
 
6
6
  <p align="center">
7
- <a href="https://github.com/cachly-dev/mcp-server/stargazers">
8
- <img src="https://img.shields.io/github/stars/cachly-dev/mcp-server?style=social" alt="GitHub Stars" />
7
+ <a href="https://github.com/cachly-dev/cachly-mcp/stargazers">
8
+ <img src="https://img.shields.io/github/stars/cachly-dev/cachly-mcp?style=social" alt="GitHub Stars" />
9
9
  </a>
10
10
  &nbsp;
11
11
  <a href="https://www.npmjs.com/package/@cachly-dev/mcp-server">
@@ -23,175 +23,123 @@
23
23
  <a href="https://cachly.dev/legal">
24
24
  <img src="https://img.shields.io/badge/GDPR-EU%20only-green" alt="GDPR: EU only" />
25
25
  </a>
26
- &nbsp;
27
- <img src="https://img.shields.io/badge/License-Apache--2.0-yellow" alt="License: Apache-2.0" />
28
26
  </p>
29
27
 
30
28
  ---
31
29
 
32
- ## The Problem
33
-
34
- Every morning, you open your AI coding assistant. It doesn't remember yesterday.
35
- You explain your architecture. You explain the deployment process. You explain the bug you fixed last week.
36
-
37
- **The average developer wastes 45 minutes/day re-establishing context.** That's โ‚ฌ15,000+ in lost productivity per engineer per year.
38
-
39
- ## The Fix โ€” One Command
30
+ ## One command. Permanent memory. No copy-paste.
40
31
 
41
32
  ```bash
42
33
  npx @cachly-dev/mcp-server@latest setup
43
34
  ```
44
35
 
45
- The interactive wizard:
46
- 1. Signs you in (free, no credit card required)
47
- 2. Picks or creates your AI Brain instance
48
- 3. **Auto-detects** Cursor, Windsurf, VS Code, Claude Code, Continue.dev
49
- 4. Writes the correct MCP config for every detected editor
50
- 5. Creates `CLAUDE.md` with memory rules pre-filled
51
-
52
- **Result:** Your AI remembers everything. Always.
36
+ Browser opens โ†’ sign in โ†’ Brain provisioned โ†’ every editor configured automatically.
37
+ No dashboard visit. No API keys to copy. No instance IDs to manage.
53
38
 
54
39
  ---
55
40
 
56
- ## Quick Start (Manual)
57
-
58
- **Step 1 โ€” Get your free credentials** at [cachly.dev](https://cachly.dev) (25 MB free, forever, no credit card).
41
+ ## What it looks like
59
42
 
60
- **Step 2 โ€” Add to your editor's MCP config:**
43
+ Every session starts with this instead of a blank slate:
61
44
 
62
- <details>
63
- <summary><b>Claude Code</b> (<code>~/.claude/mcp.json</code> or <code>.mcp.json</code>)</summary>
64
-
65
- ```json
66
- {
67
- "mcpServers": {
68
- "cachly": {
69
- "type": "stdio",
70
- "command": "npx",
71
- "args": ["-y", "@cachly-dev/mcp-server@latest"],
72
- "env": {
73
- "CACHLY_JWT": "your-jwt-token",
74
- "CACHLY_INSTANCE_ID": "your-instance-id"
75
- }
76
- }
77
- }
78
- }
79
45
  ```
80
- </details>
46
+ ๐Ÿง  Session Briefing
81
47
 
82
- <details>
83
- <summary><b>Cursor / Windsurf / VS Code</b> (<code>.cursor/mcp.json</code> / <code>.mcp.json</code>)</summary>
48
+ ๐Ÿ“… Last session (2h ago): Fixed auth redirect loop in Next.js middleware
49
+ Duration: 47 min ยท Files: middleware.ts, auth.config.ts, .env.local
84
50
 
85
- ```json
86
- {
87
- "servers": {
88
- "cachly": {
89
- "type": "stdio",
90
- "command": "npx",
91
- "args": ["-y", "@cachly-dev/mcp-server@latest"],
92
- "env": {
93
- "CACHLY_JWT": "your-jwt-token",
94
- "CACHLY_INSTANCE_ID": "your-instance-id"
95
- }
96
- }
97
- }
98
- }
99
- ```
100
- </details>
51
+ ๐Ÿ“Š Brain: 23 lessons ยท 5 context entries ยท 38 recalls ยท ~9.5h saved
101
52
 
102
- **Step 3 โ€” Add to `CLAUDE.md` / `.github/copilot-instructions.md`:**
53
+ ๐ŸŽฏ Relevant for "auth":
54
+ โœ… next:middleware-auth โ€” matcher must exclude /_next/static paths (negative lookahead)
55
+ โœ… keycloak:refresh-token โ€” use httpOnly cookie, not localStorage โ€” Safari blocks third-party
56
+ โŒ oauth:pkce โ€” PKCE + CORS broken in Safari 16.3, still unresolved
103
57
 
104
- ```markdown
105
- ## AI Brain Rules (cachly)
106
- - Call session_start BEFORE reading any files or making changes
107
- - Call learn_from_attempts AFTER every fix, deploy, or discovery
108
- - Call session_end when closing the window
58
+ ๐Ÿ• Recent lessons:
59
+ โœ… redis:pipeline-batching โ€” pipeline() for N writes, 10ร— faster than sequential
60
+ โœ… deploy:healthcheck โ€” readinessProbe.failureThreshold must be โ‰ฅ 10 on cold starts
61
+ โœ… typescript:strictNullChecks โ€” enable in tsconfig, catches 30% of runtime errors at compile time
109
62
  ```
110
63
 
64
+ Your AI arrives pre-briefed. No repeating yourself. No re-explaining the architecture.
65
+ Just: **"session_start" โ†’ full context โ†’ work.**
66
+
111
67
  ---
112
68
 
113
- ## With vs. Without cachly
69
+ ## Before vs. After
114
70
 
115
71
  | Situation | Without cachly | With cachly |
116
72
  |-----------|----------------|-------------|
117
- | Session start | "What's your architecture?" | "Ready. 23 lessons, last session: deployed API." |
118
- | Known bug hits again | Re-researches from scratch | "You fixed this on March 12, here's the exact command" |
119
- | After holiday / team handoff | Context dead | Fully briefed in < 10 seconds |
120
- | Repeated LLM patterns | Pays for each re-discovery | Cached by meaning, ~1,200 tokens saved per hit |
121
- | New team member | Weeks to onboard | `session_start` gives full context instantly |
73
+ | Session start | "What's your architecture?" | Briefed in 2s โ€” lessons, last session, open failures |
74
+ | Bug hits again | Re-researches from scratch (20โ€“40 min) | "You fixed this on March 12, exact command: `kubectl rollout restart`" |
75
+ | After holiday | Context completely dead | Full context restored, team lessons visible |
76
+ | New team member | Weeks to onboard | `session_start` gives full codebase context instantly |
77
+ | Repeated patterns | Pays tokens for each re-discovery | Recalled in 1 round-trip, ~1,200 tokens saved per hit |
122
78
 
123
79
  ---
124
80
 
125
- ## 51 MCP Tools
81
+ ## Setup Wizard โ€” What Happens
126
82
 
127
- ### ๐Ÿง  Session & Memory (most used)
83
+ ```
84
+ ๐Ÿง  cachly AI Brain โ€” Interactive Setup
85
+ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
128
86
 
129
- | Tool | What it does |
130
- |------|-------------|
131
- | **`session_start`** | Full briefing: last session summary, open failures, recent lessons, brain health |
132
- | **`session_end`** | Save what you built, auto-extract lessons from summary + ambient git log |
133
- | **`session_handoff`** | Hand off remaining tasks to next window, with context |
134
- | **`auto_learn_session`** | Batch-learn from a list of observations |
135
- | **`sync_file_changes`** | Sync changed files into brain index |
136
- | **`learn_from_attempts`** | Store structured lessons after any fix, deploy, or discovery |
137
- | **`recall_best_solution`** | Best known solution for a topic โ€” with success/failure history |
138
- | **`recall_at`** | Recall a lesson at a specific point in time |
139
- | **`remember_context`** | Cache architecture findings, decisions, file summaries |
140
- | **`recall_context`** | Get exact context by key (supports glob) |
141
- | **`list_remembered`** | See all cached context entries |
142
- | **`forget_context`** | Remove stale context |
143
- | **`smart_recall`** | BM25+ full-text search across all brain data |
144
- | **`setup_ai_memory`** | Interactive wizard to configure AI memory |
145
-
146
- ### ๐Ÿ’ก Legendary Brain Features
147
-
148
- | Feature | How it works |
149
- |---------|-------------|
150
- | **Team Telepathy** | Multi-dev Brain: the `author` param on `learn_from_attempts` shares fixes across the whole team. Every `session_start` shows colleagues' lessons. |
151
- | **Ambient Git Learning** | Pass `workspace_path` to `session_end` โ€” it auto-reads `git log`, stores recent commits as Brain lessons. Zero extra calls. |
152
- | **Memory Crystals** | `memory_crystalize` distills all lessons into a compact Crystal injected at every `session_start`. AI arrives pre-loaded with team knowledge. |
153
- | **IQ Boost** | `brain_doctor` reports `iq_boost_pct` โ€” how much smarter your AI gets vs. baseline from cached lessons. |
87
+ Step 1: Sign in to cachly (free โ€” no credit card required)
88
+ Code: XKCD-9281
89
+ URL: https://auth.cachly.dev/activate?user_code=XKCD-9281
90
+ โœ“ Browser opened โ€” confirm the code above to continue...
91
+ ................. โœ“ Authorized!
92
+ โณ Generating your API key... โœ“
154
93
 
155
- ### โš™๏ธ Instance Management
94
+ โณ Fetching your instances... found 1
156
95
 
157
- | Tool | What it does |
158
- |------|-------------|
159
- | `list_instances` | List all your cache instances |
160
- | `create_instance` | Spin up a new instance (free or paid) |
161
- | `get_connection_string` | Get the `redis://` URL for your app |
162
- | `delete_instance` | Remove an instance |
163
- | `get_real_time_stats` | Memory, hit rate, ops/sec |
96
+ โœ“ Instance: My Brain (a1b2c3d4โ€ฆ)
97
+
98
+ Step 3: Detected editors: Claude Code, Cursor, GitHub Copilot
99
+ Configure for which? [all / claude/cursor/copilot] (Enter = all):
100
+
101
+ โœ… Written: .mcp.json
102
+ โœ… Written: .cursor/mcp.json
103
+ โœ… Written: CLAUDE.md
104
+
105
+ ๐Ÿง  Done! Restart your editor โ€” the `cachly` MCP tools will appear.
106
+ Your AI now has persistent memory across every session.
107
+ ```
164
108
 
165
- ### ๐Ÿ—„๏ธ Cache Operations
109
+ ---
110
+
111
+ ## Tools (most important ones)
112
+
113
+ ### Session & Memory
166
114
 
167
115
  | Tool | What it does |
168
116
  |------|-------------|
169
- | `cache_get` / `cache_set` / `cache_delete` | Standard cache operations |
170
- | `cache_mget` / `cache_mset` | Bulk pipeline (single round-trip) |
171
- | `cache_lock_acquire` / `cache_lock_release` | Distributed Redlock-lite |
172
- | `cache_stream_set` / `cache_stream_get` | LLM token stream caching |
117
+ | **`session_start`** | Full briefing: last session, open failures, relevant lessons, brain stats |
118
+ | **`session_end`** | Save what you built, auto-extract lessons from summary + git log |
119
+ | **`learn_from_attempts`** | Store lessons after any fix, deploy, or discovery |
120
+ | **`recall_best_solution`** | Best known fix for a topic โ€” with full success/failure history |
121
+ | **`smart_recall`** | Full-text search across all brain data (BM25+) |
122
+ | **`remember_context`** / **`recall_context`** | Save/restore architecture findings, file summaries, ADRs |
123
+ | **`forget_lesson`** | Remove a stale or wrong lesson |
124
+ | **`list_lessons`** | Overview of all stored topics with severity, age, recall count |
173
125
 
174
- ### ๐Ÿ” Semantic Cache
126
+ ### Team Brain
175
127
 
176
128
  | Tool | What it does |
177
129
  |------|-------------|
178
- | `semantic_search` | Find cached entries by meaning (pgvector HNSW, EU-hosted) |
179
- | `semantic_warmup` | Pre-warm cache with prompt/response pairs |
180
- | `detect_namespace` | Auto-classify prompt into code/qa/summary/translation/creative |
130
+ | **`team_learn`** / **`team_recall`** | Share lessons across the whole team on a shared instance |
131
+ | **`memory_crystalize`** | Distill all lessons into a Crystal snapshot โ€” injected at every `session_start` |
132
+ | **`brain_doctor`** | Health check: IQ boost %, lesson quality, open failures |
133
+ | **`invite_member`** | Invite a dev to your org by email |
181
134
 
182
- ### ๐Ÿ‘ฅ Team Brain
135
+ ### Cache & Infrastructure
183
136
 
184
137
  | Tool | What it does |
185
138
  |------|-------------|
186
- | `team_learn` / `team_recall` | Share lessons across the team |
187
- | `team_synthesize` | Consolidate multiple lessons into one authoritative version |
188
- | `memory_crystalize` | Distill all lessons into a Crystal snapshot for instant team context |
189
- | `brain_doctor` | Health check: lesson count, IQ boost %, open failures, quality score |
190
- | `global_learn` / `global_recall` | Cross-project universal lessons |
191
- | `publish_lesson` / `import_public_brain` | Share/import community knowledge |
192
- | `trace_dependency` | Causal chain โ€” find lessons affected by a dependency change |
193
- | `list_orgs` / `create_org` | Manage team organizations |
194
- | `invite_member` / `get_org_plan` | Invite a developer to your org by email |
139
+ | `cache_get` / `cache_set` / `cache_delete` | Standard Redis operations |
140
+ | `cache_mget` / `cache_mset` | Bulk pipeline (single round-trip) |
141
+ | `semantic_search` | Find cached entries by meaning (vector search, EU-hosted) |
142
+ | `get_connection_string` | Get the `redis://` URL for your own app |
195
143
 
196
144
  ---
197
145
 
@@ -199,13 +147,69 @@ The interactive wizard:
199
147
 
200
148
  | Tier | RAM | Price | Best for |
201
149
  |------|-----|-------|----------|
202
- | **Free** | 25 MB | **โ‚ฌ0/mo forever** | Dev & side projects |
150
+ | **Free** | 25 MB | **โ‚ฌ0/mo forever** | Side projects & solo devs |
203
151
  | **Dev** | 200 MB | โ‚ฌ19/mo | Individual developers |
204
- | **Pro** | 900 MB | โ‚ฌ49/mo | Teams |
205
- | **Speed** | 900 MB + Dragonfly + Semantic Cache | โ‚ฌ79/mo | AI-heavy workloads |
152
+ | **Pro** | 900 MB | โ‚ฌ49/mo | Teams (shared brain) |
153
+ | **Speed** | 900 MB + Dragonfly + Semantic | โ‚ฌ79/mo | AI-heavy workloads |
206
154
  | **Business** | 7 GB | โ‚ฌ199/mo | Scale-ups |
207
155
 
208
- โœ… All plans: **German servers ยท GDPR-compliant ยท 99.9% SLA ยท No credit card for Free tier**
156
+ โœ… All plans: **German servers ยท GDPR-compliant ยท 99.9% SLA ยท no credit card for Free**
157
+
158
+ ---
159
+
160
+ ## Manual setup (if you prefer)
161
+
162
+ <details>
163
+ <summary>Claude Code (<code>~/.claude/mcp.json</code> or <code>.mcp.json</code>)</summary>
164
+
165
+ ```json
166
+ {
167
+ "mcpServers": {
168
+ "cachly": {
169
+ "type": "stdio",
170
+ "command": "npx",
171
+ "args": ["-y", "@cachly-dev/mcp-server@latest"],
172
+ "env": {
173
+ "CACHLY_JWT": "your-api-key",
174
+ "CACHLY_BRAIN_INSTANCE_ID": "your-instance-id"
175
+ }
176
+ }
177
+ }
178
+ }
179
+ ```
180
+ </details>
181
+
182
+ <details>
183
+ <summary>Cursor / Windsurf (<code>.cursor/mcp.json</code> or <code>.mcp.json</code>)</summary>
184
+
185
+ ```json
186
+ {
187
+ "servers": {
188
+ "cachly": {
189
+ "type": "stdio",
190
+ "command": "npx",
191
+ "args": ["-y", "@cachly-dev/mcp-server@latest"],
192
+ "env": {
193
+ "CACHLY_JWT": "your-api-key",
194
+ "CACHLY_BRAIN_INSTANCE_ID": "your-instance-id"
195
+ }
196
+ }
197
+ }
198
+ }
199
+ ```
200
+ </details>
201
+
202
+ <details>
203
+ <summary>CLAUDE.md brain rules</summary>
204
+
205
+ ```markdown
206
+ ## AI Brain (cachly)
207
+ - ALWAYS call `session_start` at the beginning of every session โ€” no exceptions
208
+ - After every fix, deploy, or discovery: call `learn_from_attempts`
209
+ - Before working on a known topic: call `recall_best_solution("<topic>")`
210
+ - At session end: call `session_end` with a one-line summary
211
+ ```
212
+ </details>
209
213
 
210
214
  ---
211
215
 
@@ -213,30 +217,27 @@ The interactive wizard:
213
217
 
214
218
  | Variable | Default | Description |
215
219
  |----------|---------|-------------|
216
- | `CACHLY_JWT` | โ€” | **Required.** Your API token from [cachly.dev](https://cachly.dev) |
217
- | `CACHLY_INSTANCE_ID` | โ€” | Default instance UUID (optional if you pass per-call) |
220
+ | `CACHLY_JWT` | โ€” | **Required.** API key or JWT from the setup wizard |
221
+ | `CACHLY_BRAIN_INSTANCE_ID` | โ€” | Default instance UUID โ€” set once, works in all tools |
218
222
  | `CACHLY_API_URL` | `https://api.cachly.dev` | Override for self-hosted |
219
- | `CACHLY_NO_TELEMETRY` | unset | Set to `1` to disable anonymous usage pings |
220
- | `CACHLY_NO_UPDATE_CHECK` | unset | Set to `1` to disable the version-check on startup |
223
+ | `CACHLY_NO_TELEMETRY` | unset | Set to `1` to opt out of anonymous usage pings |
221
224
 
222
225
  ---
223
226
 
224
- ## ๐Ÿ› ๏ธ Ecosystem
227
+ ## Ecosystem
225
228
 
226
229
  | Package | What it does |
227
230
  |---------|-------------|
228
- | **[`@cachly-dev/mcp-server`](https://www.npmjs.com/package/@cachly-dev/mcp-server)** | โ† you are here โ€” AI Brain MCP tools for Claude Code, Cursor, Copilot, Windsurf |
229
- | **[`@cachly-dev/openclaw`](https://www.npmjs.com/package/@cachly-dev/openclaw)** | JS/TS SDK โ€” cut LLM costs 60โ€“90% with semantic cache + BM25 in your own app, 3 lines of code |
230
- | **[`@cachly-dev/init`](https://www.npmjs.com/package/@cachly-dev/init)** | One-command setup wizard: auto-detects all your editors and writes the correct MCP config โ€” `npx @cachly-dev/init` |
231
- | **[`@cachly-dev/cli`](https://www.npmjs.com/package/@cachly-dev/cli)** | Terminal CLI โ€” manage instances, cache data, brain lessons: `npm i -g @cachly-dev/cli` |
232
- | **[`@cachly-dev/sdk`](https://www.npmjs.com/package/@cachly-dev/sdk)** | Node.js/TypeScript SDK โ€” use managed Redis, semantic cache & AI memory directly in your app |
231
+ | **[`@cachly-dev/mcp-server`](https://www.npmjs.com/package/@cachly-dev/mcp-server)** | โ† you are here โ€” AI Brain MCP for Claude Code, Cursor, Copilot, Windsurf |
232
+ | **[`cachly-brain` (VS Code)](https://marketplace.visualstudio.com/items?itemName=cachly.cachly-brain)** | VS Code extension โ€” status bar, CodeLens lessons, one-click setup |
233
+ | **[`@cachly-dev/openclaw`](https://www.npmjs.com/package/@cachly-dev/openclaw)** | JS/TS SDK โ€” cut LLM costs 60โ€“90% with semantic cache in your own app |
234
+ | **[`@cachly-dev/sdk`](https://www.npmjs.com/package/@cachly-dev/sdk)** | Node.js SDK โ€” managed Redis, semantic cache & AI memory in your app |
233
235
 
234
236
  ---
235
237
 
236
238
  ## Links
237
239
 
238
240
  - ๐ŸŒ [cachly.dev](https://cachly.dev) โ€” Dashboard & free signup
239
- - ๐Ÿ“– [AI Brain docs](https://cachly.dev/docs/ai-memory) โ€” Full documentation
240
- - ๐Ÿ’ฌ [GitHub Issues](https://github.com/cachly-dev/mcp-server/issues) โ€” Bug reports & feature requests
241
- - โญ [Star on GitHub](https://github.com/cachly-dev/mcp-server) โ€” If cachly saves you time, a star means a lot!
242
- - ๐Ÿ“ฆ [npm](https://www.npmjs.com/package/@cachly-dev/mcp-server)
241
+ - ๐Ÿ“– [Docs](https://cachly.dev/docs/ai-memory) โ€” Full documentation
242
+ - ๐Ÿ’ฌ [Issues](https://github.com/cachly-dev/cachly-mcp/issues) โ€” Bug reports & feature requests
243
+ - โญ [Star on GitHub](https://github.com/cachly-dev/cachly-mcp) โ€” If cachly saves you time, a star means a lot
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { randomUUID } from 'node:crypto';
3
3
  import { readdir, readFile, stat } from 'node:fs/promises';
4
4
  import { join, relative, extname } from 'node:path';
5
5
  /**
6
- * cachly MCP Server v0.5.39
6
+ * cachly MCP Server v0.5.41
7
7
  *
8
8
  * Exposes cachly.dev as MCP tools so any AI assistant
9
9
  * (GitHub Copilot, Claude, Cursor, Windsurf, Continue.dev โ€ฆ) can:
@@ -609,11 +609,25 @@ async function keywordSearch(redis, patterns, query, topK = 10) {
609
609
  // โ”€โ”€ Exported for testing โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
610
610
  export { tokenize, splitMultiQuery, levenshtein, recencyBoost, extractTimestamp, STOPWORDS };
611
611
  // โ”€โ”€ Connection pool โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
612
- /** Reuse Redis connections across tool calls (keyed by instance_id). */
612
+ /** Reuse Redis connections across tool calls (keyed by instance_id). Max 20 entries, LRU eviction. */
613
613
  const pool = new Map();
614
+ const POOL_MAX = 20;
614
615
  async function getConnection(instance_id) {
615
- if (pool.has(instance_id))
616
- return pool.get(instance_id);
616
+ if (pool.has(instance_id)) {
617
+ // Move to end (most-recently-used)
618
+ const conn = pool.get(instance_id);
619
+ pool.delete(instance_id);
620
+ pool.set(instance_id, conn);
621
+ return conn;
622
+ }
623
+ // Evict oldest entry when pool is full
624
+ if (pool.size >= POOL_MAX) {
625
+ const oldest = pool.keys().next().value;
626
+ if (oldest) {
627
+ pool.get(oldest)?.quit().catch(() => undefined);
628
+ pool.delete(oldest);
629
+ }
630
+ }
617
631
  const inst = await apiFetch(`/api/v1/instances/${instance_id}`);
618
632
  if (inst.status !== 'running') {
619
633
  throw new McpError(ErrorCode.InvalidRequest, `Instance "${inst.name}" is not running (status: ${inst.status}). ` +
@@ -2031,31 +2045,33 @@ async function handleTool(name, args) {
2031
2045
  await redis.set(cacheKey, content);
2032
2046
  await redis.set(`${cacheKey}:meta`, meta);
2033
2047
  }
2034
- // Also index semantically for smart_recall (if vector available)
2035
- const inst = await apiFetch(`/api/v1/instances/${instance_id}`);
2036
- if (inst.vector_token) {
2037
- try {
2038
- const embedding = await computeEmbedding(`${key}: ${content.slice(0, 500)}`);
2039
- const vectorUrl = `https://api.cachly.dev/v1/sem/${inst.vector_token}`;
2040
- const body = {
2041
- id: `ctx:${category}:${key}`,
2042
- prompt: key,
2043
- namespace: 'cachly:ctx',
2044
- embedding,
2045
- };
2046
- if (ttl && ttl > 0) {
2047
- body['expires_at'] = new Date(Date.now() + ttl * 1000).toISOString();
2048
+ // Also index semantically for smart_recall (if vector available and embedding is configured)
2049
+ if (EMBED_PROVIDER !== 'none') {
2050
+ const inst = await apiFetch(`/api/v1/instances/${instance_id}`);
2051
+ if (inst.vector_token) {
2052
+ try {
2053
+ const embedding = await computeEmbedding(`${key}: ${content.slice(0, 500)}`);
2054
+ const vectorUrl = `https://api.cachly.dev/v1/sem/${inst.vector_token}`;
2055
+ const body = {
2056
+ id: `ctx:${category}:${key}`,
2057
+ prompt: key,
2058
+ namespace: 'cachly:ctx',
2059
+ embedding,
2060
+ };
2061
+ if (ttl && ttl > 0) {
2062
+ body['expires_at'] = new Date(Date.now() + ttl * 1000).toISOString();
2063
+ }
2064
+ await fetch(`${vectorUrl}/entries`, {
2065
+ method: 'POST',
2066
+ headers: { 'Content-Type': 'application/json' },
2067
+ body: JSON.stringify(body),
2068
+ }).catch(() => undefined);
2069
+ }
2070
+ catch {
2071
+ // Embedding optional โ€” continue silently
2048
2072
  }
2049
- await fetch(`${vectorUrl}/entries`, {
2050
- method: 'POST',
2051
- headers: { 'Content-Type': 'application/json' },
2052
- body: JSON.stringify(body),
2053
- }).catch(() => undefined);
2054
- }
2055
- catch {
2056
- // Embedding optional โ€” continue silently
2057
2073
  }
2058
- }
2074
+ } // end EMBED_PROVIDER !== 'none'
2059
2075
  return [
2060
2076
  `๐Ÿง  **Context Saved**`,
2061
2077
  ``,
@@ -2642,7 +2658,18 @@ async function handleTool(name, args) {
2642
2658
  lines.push('');
2643
2659
  }
2644
2660
  else {
2645
- lines.push(`๐Ÿ“Š **Brain:** ${lessons.length} lessons ยท ${ctxCount} context entries`, '');
2661
+ const totalRecalls = lessons.reduce((s, l) => s + (l.recall_count ?? 0), 0);
2662
+ const savedMin = totalRecalls * 15;
2663
+ const savedStr = savedMin >= 60
2664
+ ? `~${(savedMin / 60).toFixed(1)}h saved`
2665
+ : savedMin > 0 ? `~${savedMin}min saved` : '';
2666
+ const statsLine = [
2667
+ `${lessons.length} lessons`,
2668
+ ctxCount > 0 ? `${ctxCount} context entries` : '',
2669
+ totalRecalls > 0 ? `${totalRecalls} recalls` : '',
2670
+ savedStr,
2671
+ ].filter(Boolean).join(' ยท ');
2672
+ lines.push(`๐Ÿ“Š **Brain:** ${statsLine}`, '');
2646
2673
  }
2647
2674
  // Focus-relevant lessons
2648
2675
  if (focusLessons.length > 0) {
@@ -2800,21 +2827,29 @@ async function handleTool(name, args) {
2800
2827
  lStream.on('error', reject);
2801
2828
  });
2802
2829
  const relevant = [];
2803
- for (const k of lessonKeys) {
2804
- const raw = await redis.get(k);
2805
- if (!raw)
2806
- continue;
2807
- const lesson = JSON.parse(raw);
2808
- // Match by file_paths stored in lesson OR by topic keywords matching file name
2809
- const topicWords = lesson.topic.toLowerCase().split(/[:\-_]/);
2810
- const fileMatches = changed_files.some(f => {
2811
- const fname = f.split('/').pop()?.replace(/\.[^.]+$/, '').toLowerCase() ?? '';
2812
- return topicWords.some(w => w.length > 3 && fname.includes(w))
2813
- || (lesson.file_paths ?? []).some(lf => f.includes(lf) || lf.includes(f));
2814
- });
2815
- if (fileMatches) {
2816
- const emoji = lesson.outcome === 'success' ? 'โœ…' : 'โš ๏ธ';
2817
- relevant.push(` ${emoji} \`${lesson.topic}\` โ€” ${lesson.what_worked.slice(0, 80)}`);
2830
+ if (lessonKeys.length > 0) {
2831
+ const values = await redis.mget(...lessonKeys);
2832
+ for (const raw of values) {
2833
+ if (!raw)
2834
+ continue;
2835
+ let lesson;
2836
+ try {
2837
+ lesson = JSON.parse(raw);
2838
+ }
2839
+ catch {
2840
+ continue;
2841
+ }
2842
+ // Match by file_paths stored in lesson OR by topic keywords matching file name
2843
+ const topicWords = lesson.topic.toLowerCase().split(/[:\-_]/);
2844
+ const fileMatches = changed_files.some(f => {
2845
+ const fname = f.split('/').pop()?.replace(/\.[^.]+$/, '').toLowerCase() ?? '';
2846
+ return topicWords.some(w => w.length > 3 && fname.includes(w))
2847
+ || (lesson.file_paths ?? []).some(lf => f.includes(lf) || lf.includes(f));
2848
+ });
2849
+ if (fileMatches) {
2850
+ const emoji = lesson.outcome === 'success' ? 'โœ…' : 'โš ๏ธ';
2851
+ relevant.push(` ${emoji} \`${lesson.topic}\` โ€” ${lesson.what_worked.slice(0, 80)}`);
2852
+ }
2818
2853
  }
2819
2854
  }
2820
2855
  const lines = [
@@ -2850,7 +2885,7 @@ async function handleTool(name, args) {
2850
2885
  file_paths: file_paths ?? [],
2851
2886
  commands: commands ?? [],
2852
2887
  tags: [...(tags ?? []), 'team'],
2853
- timestamp: new Date().toISOString(),
2888
+ ts: new Date().toISOString(),
2854
2889
  recall_count: 0,
2855
2890
  version: 2,
2856
2891
  };
@@ -2874,14 +2909,16 @@ async function handleTool(name, args) {
2874
2909
  lStream.on('error', reject);
2875
2910
  });
2876
2911
  let lessons = [];
2877
- for (const k of lessonKeys) {
2878
- const raw = await redis.get(k);
2879
- if (!raw)
2880
- continue;
2881
- try {
2882
- lessons.push(JSON.parse(raw));
2912
+ if (lessonKeys.length > 0) {
2913
+ const values = await redis.mget(...lessonKeys);
2914
+ for (const raw of values) {
2915
+ if (!raw)
2916
+ continue;
2917
+ try {
2918
+ lessons.push(JSON.parse(raw));
2919
+ }
2920
+ catch { /* skip */ }
2883
2921
  }
2884
- catch { /* skip */ }
2885
2922
  }
2886
2923
  // Filter
2887
2924
  if (topic) {