@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 +148 -147
- package/dist/index.js +88 -51
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +57 -24
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# cachly โ Give Your AI a Permanent Brain
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
>
|
|
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
|
|
8
|
-
<img src="https://img.shields.io/github/stars/cachly-dev/mcp
|
|
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
|
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
46
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
46
|
+
๐ง Session Briefing
|
|
81
47
|
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
##
|
|
69
|
+
## Before vs. After
|
|
114
70
|
|
|
115
71
|
| Situation | Without cachly | With cachly |
|
|
116
72
|
|-----------|----------------|-------------|
|
|
117
|
-
| Session start | "What's your architecture?" |
|
|
118
|
-
|
|
|
119
|
-
| After holiday
|
|
120
|
-
|
|
|
121
|
-
|
|
|
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
|
-
##
|
|
81
|
+
## Setup Wizard โ What Happens
|
|
126
82
|
|
|
127
|
-
|
|
83
|
+
```
|
|
84
|
+
๐ง cachly AI Brain โ Interactive Setup
|
|
85
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
128
86
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
94
|
+
โณ Fetching your instances... found 1
|
|
156
95
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Tools (most important ones)
|
|
112
|
+
|
|
113
|
+
### Session & Memory
|
|
166
114
|
|
|
167
115
|
| Tool | What it does |
|
|
168
116
|
|------|-------------|
|
|
169
|
-
|
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
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
|
-
###
|
|
126
|
+
### Team Brain
|
|
175
127
|
|
|
176
128
|
| Tool | What it does |
|
|
177
129
|
|------|-------------|
|
|
178
|
-
|
|
|
179
|
-
|
|
|
180
|
-
|
|
|
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
|
-
###
|
|
135
|
+
### Cache & Infrastructure
|
|
183
136
|
|
|
184
137
|
| Tool | What it does |
|
|
185
138
|
|------|-------------|
|
|
186
|
-
| `
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
189
|
-
| `
|
|
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** |
|
|
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
|
|
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 ยท
|
|
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.**
|
|
217
|
-
| `
|
|
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
|
|
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
|
-
##
|
|
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
|
|
229
|
-
| **[
|
|
230
|
-
| **[`@cachly-dev/
|
|
231
|
-
| **[`@cachly-dev/
|
|
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
|
-
- ๐ [
|
|
240
|
-
- ๐ฌ [
|
|
241
|
-
- โญ [Star on GitHub](https://github.com/cachly-dev/mcp
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2804
|
-
const
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
const
|
|
2817
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2878
|
-
const
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
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) {
|