@memly/mcp-server 0.2.1 → 0.2.3
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 +85 -41
- package/dist/index.js +340 -63
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,16 +1,49 @@
|
|
|
1
1
|
# @memly/mcp-server
|
|
2
2
|
|
|
3
|
-
> Persistent memory for
|
|
3
|
+
> Persistent memory for IDEs that **don't support custom LLM providers**.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Proxy vs MCP — which one should I use?
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
|
10
|
-
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
|
|
7
|
+
Memly has two integration modes:
|
|
8
|
+
|
|
9
|
+
| Mode | How it works | Best for |
|
|
10
|
+
|------|-------------|----------|
|
|
11
|
+
| **Proxy** (transparent) | Point your IDE's base URL to `api.memly.site` — memory injected in 100% of requests, invisible | IDEs that support a custom OpenAI-compatible endpoint |
|
|
12
|
+
| **MCP server** (this package) | Memly runs as an MCP tool — `load_context` is called automatically at session start | IDEs locked to their own AI service |
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Which mode for my IDE?
|
|
17
|
+
|
|
18
|
+
### ✅ Use the Proxy — supports custom endpoint
|
|
19
|
+
|
|
20
|
+
| IDE / Tool | Where to configure |
|
|
21
|
+
|-----------|-------------------|
|
|
22
|
+
| **Cursor** | Settings → Models → OpenAI Base URL |
|
|
23
|
+
| **Continue.dev** (VS Code / JetBrains extension) | `config.json` → `apiBase` |
|
|
24
|
+
| **Zed** | `settings.json` → `api_url` |
|
|
25
|
+
| **Aider** | `--openai-api-base` CLI flag |
|
|
26
|
+
| **Jan.ai** | Settings → Model → Engine URL |
|
|
27
|
+
| **LM Studio** | Server settings |
|
|
28
|
+
| **Open WebUI** | Admin → Connections |
|
|
29
|
+
| **Msty** | Settings → Custom provider |
|
|
30
|
+
| **Void** | Settings → Custom provider |
|
|
31
|
+
|
|
32
|
+
### ❌ Use the MCP server — locked provider, no custom endpoint
|
|
33
|
+
|
|
34
|
+
| IDE / Tool | Reason |
|
|
35
|
+
|-----------|--------|
|
|
36
|
+
| **VS Code + GitHub Copilot** | Microsoft/GitHub auth — base URL not configurable |
|
|
37
|
+
| **JetBrains AI Assistant** | Locked to JetBrains AI Service subscription |
|
|
38
|
+
| **Windsurf (Codeium)** | Cascade uses Codeium's own models — no external endpoint |
|
|
39
|
+
| **Amazon Q / CodeWhisperer** | AWS-only pipeline |
|
|
40
|
+
| **Tabnine** | Proprietary closed service |
|
|
41
|
+
| **Replit AI** | Built into Replit, no external provider |
|
|
42
|
+
| **Gitpod AI** | Locked to their own service |
|
|
43
|
+
| **Sourcegraph Cody** | Locked on free tier |
|
|
44
|
+
| **Claude Desktop** | Chat app, not an IDE — MCP native |
|
|
45
|
+
|
|
46
|
+
---
|
|
14
47
|
|
|
15
48
|
## Quick Start
|
|
16
49
|
|
|
@@ -18,19 +51,25 @@
|
|
|
18
51
|
|
|
19
52
|
Go to [memly.site/dashboard/api-keys](https://memly.site/dashboard/api-keys)
|
|
20
53
|
|
|
21
|
-
### 2.
|
|
54
|
+
### 2. Configure your IDE's MCP server
|
|
22
55
|
|
|
23
|
-
|
|
56
|
+
**VS Code / GitHub Copilot** — create `.vscode/mcp.json` in your project:
|
|
24
57
|
|
|
25
|
-
```
|
|
26
|
-
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"servers": {
|
|
61
|
+
"memly": {
|
|
62
|
+
"command": "npx",
|
|
63
|
+
"args": ["-y", "@memly/mcp-server"],
|
|
64
|
+
"env": {
|
|
65
|
+
"MEMLY_API_KEY": "memly_your_key_here"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
27
70
|
```
|
|
28
71
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
### 3. Configure your IDE's MCP server
|
|
32
|
-
|
|
33
|
-
**VS Code / Copilot** — create `.vscode/mcp.json`:
|
|
72
|
+
**JetBrains** (IntelliJ, WebStorm, PyCharm…) — create `.idea/mcp.json`:
|
|
34
73
|
|
|
35
74
|
```json
|
|
36
75
|
{
|
|
@@ -46,8 +85,9 @@ This creates the right file for your IDE so Memly loads context automatically on
|
|
|
46
85
|
}
|
|
47
86
|
```
|
|
48
87
|
|
|
49
|
-
**
|
|
50
|
-
- Command: `npx
|
|
88
|
+
**Windsurf** — Settings → Cascade → MCP Servers → Add:
|
|
89
|
+
- Command: `npx`
|
|
90
|
+
- Args: `-y @memly/mcp-server`
|
|
51
91
|
- Env: `MEMLY_API_KEY=memly_your_key_here`
|
|
52
92
|
|
|
53
93
|
**Claude Desktop** — edit `claude_desktop_config.json`:
|
|
@@ -66,38 +106,42 @@ This creates the right file for your IDE so Memly loads context automatically on
|
|
|
66
106
|
}
|
|
67
107
|
```
|
|
68
108
|
|
|
69
|
-
###
|
|
109
|
+
### 3. Run auto-setup once per project
|
|
70
110
|
|
|
71
|
-
|
|
111
|
+
Writes the instruction file for your IDE so `load_context` runs automatically at every session start:
|
|
72
112
|
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
At the start of every session, you MUST:
|
|
76
|
-
1. Call `memly.search_memories({ query: "current active task", limit: 5 })`
|
|
77
|
-
2. Use this context to understand the project state.
|
|
78
|
-
3. When you make major decisions, call `memly.save_memory`.
|
|
113
|
+
```bash
|
|
114
|
+
npx @memly/mcp-server init
|
|
79
115
|
```
|
|
80
116
|
|
|
81
|
-
|
|
117
|
+
Auto-detects VS Code, Cursor, Windsurf, Claude Desktop. Run once, never think about it again.
|
|
118
|
+
|
|
119
|
+
---
|
|
82
120
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
|
86
|
-
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
121
|
+
## Tools
|
|
122
|
+
|
|
123
|
+
| Tool | When the AI calls it |
|
|
124
|
+
|------|---------------------|
|
|
125
|
+
| `load_context` | **Automatically at session start** — loads memories from previous sessions |
|
|
126
|
+
| `search_memories` | When you ask about something specific not loaded by `load_context` |
|
|
127
|
+
| `remember` | When you make a decision, solve a problem, or say "remember this" |
|
|
128
|
+
| `list_projects` | When you ask to list or switch projects |
|
|
129
|
+
|
|
130
|
+
---
|
|
89
131
|
|
|
90
132
|
## Environment Variables
|
|
91
133
|
|
|
92
134
|
| Variable | Required | Default | Description |
|
|
93
|
-
|
|
94
|
-
| `MEMLY_API_KEY` | ✅ | — | Your Memly API key |
|
|
95
|
-
| `MEMLY_API_URL` | — | `https://api.memly.site` |
|
|
96
|
-
| `MEMLY_PORT` | — | `3800` | HTTP transport port |
|
|
135
|
+
|----------|----------|---------|-------------|
|
|
136
|
+
| `MEMLY_API_KEY` | ✅ | — | Your Memly API key (`memly_...`) |
|
|
137
|
+
| `MEMLY_API_URL` | — | `https://api.memly.site` | Override for self-hosted deployments |
|
|
138
|
+
| `MEMLY_PORT` | — | `3800` | HTTP transport port (`--http` mode only) |
|
|
139
|
+
|
|
140
|
+
---
|
|
97
141
|
|
|
98
|
-
## Self-Hosted
|
|
142
|
+
## Self-Hosted
|
|
99
143
|
|
|
100
|
-
Run alongside your Memly proxy for zero-latency:
|
|
144
|
+
Run alongside your Memly proxy on your VPS for zero-latency:
|
|
101
145
|
|
|
102
146
|
```bash
|
|
103
147
|
MEMLY_API_KEY=memly_... bun run packages/mcp-server/src/index.ts --http
|
package/dist/index.js
CHANGED
|
@@ -20211,93 +20211,286 @@ I am ready to continue our work.`
|
|
|
20211
20211
|
}
|
|
20212
20212
|
|
|
20213
20213
|
// src/init.ts
|
|
20214
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
|
|
20214
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from "node:fs";
|
|
20215
20215
|
import { join, resolve } from "node:path";
|
|
20216
20216
|
import { homedir, platform } from "node:os";
|
|
20217
20217
|
var MEMLY_INSTRUCTION_BLOCK = `
|
|
20218
|
-
## Memly
|
|
20218
|
+
## Memly — Persistent Memory (Auto-Enabled)
|
|
20219
20219
|
|
|
20220
20220
|
You have access to the Memly MCP server (\`memly\`).
|
|
20221
20221
|
|
|
20222
|
-
**RULES (always follow
|
|
20222
|
+
**RULES (always follow):**
|
|
20223
20223
|
|
|
20224
|
-
1. **At the start of EVERY conversation**, call \`load_context\` before answering
|
|
20225
|
-
-
|
|
20226
|
-
-
|
|
20227
|
-
- Otherwise call with no arguments.
|
|
20224
|
+
1. **At the start of EVERY conversation**, call \`load_context\` before answering.
|
|
20225
|
+
- Pass \`topic\` if the user's message has a clear subject (e.g., \`topic: "authentication"\`)
|
|
20226
|
+
- Otherwise call with no arguments
|
|
20228
20227
|
|
|
20229
|
-
2. **During the conversation**, call \`remember\` proactively
|
|
20230
|
-
-
|
|
20228
|
+
2. **During the conversation**, call \`remember\` proactively when:
|
|
20229
|
+
- User makes an architectural or technical decision
|
|
20231
20230
|
- You solve a non-trivial bug or problem
|
|
20232
|
-
-
|
|
20233
|
-
-
|
|
20231
|
+
- User shares important project context, preferences, or constraints
|
|
20232
|
+
- User explicitly says "remember this"
|
|
20234
20233
|
|
|
20235
|
-
3. **Never** ask
|
|
20234
|
+
3. **Never** ask if you should remember something — just do it silently.
|
|
20236
20235
|
`.trim();
|
|
20236
|
+
async function validateApiKey(apiKey) {
|
|
20237
|
+
try {
|
|
20238
|
+
const controller = new AbortController;
|
|
20239
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
20240
|
+
const response = await fetch("https://api.memly.site/health", {
|
|
20241
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
20242
|
+
signal: controller.signal
|
|
20243
|
+
});
|
|
20244
|
+
clearTimeout(timeout);
|
|
20245
|
+
return response.ok;
|
|
20246
|
+
} catch {
|
|
20247
|
+
return false;
|
|
20248
|
+
}
|
|
20249
|
+
}
|
|
20237
20250
|
function writeCopilotInstructions(projectRoot) {
|
|
20238
20251
|
const githubDir = join(projectRoot, ".github");
|
|
20239
20252
|
const filePath = join(githubDir, "copilot-instructions.md");
|
|
20240
|
-
if (!existsSync(githubDir))
|
|
20241
|
-
mkdirSync(githubDir, { recursive: true });
|
|
20242
20253
|
const MARKER = "<!-- memly-instructions -->";
|
|
20243
|
-
|
|
20244
|
-
|
|
20245
|
-
|
|
20246
|
-
return { ide: "VS Code / GitHub Copilot", file: filePath, status: "skipped" };
|
|
20254
|
+
try {
|
|
20255
|
+
if (!existsSync(githubDir)) {
|
|
20256
|
+
mkdirSync(githubDir, { recursive: true });
|
|
20247
20257
|
}
|
|
20248
|
-
|
|
20258
|
+
if (existsSync(filePath)) {
|
|
20259
|
+
const existing = readFileSync(filePath, "utf-8");
|
|
20260
|
+
if (existing.includes(MARKER)) {
|
|
20261
|
+
return { ide: "VS Code / GitHub Copilot", file: filePath, status: "skipped" };
|
|
20262
|
+
}
|
|
20263
|
+
writeFileSync(filePath, `${existing.trimEnd()}
|
|
20249
20264
|
|
|
20250
20265
|
${MARKER}
|
|
20251
20266
|
${MEMLY_INSTRUCTION_BLOCK}
|
|
20252
20267
|
${MARKER}
|
|
20253
20268
|
`);
|
|
20254
|
-
|
|
20255
|
-
|
|
20256
|
-
|
|
20269
|
+
return { ide: "VS Code / GitHub Copilot", file: filePath, status: "updated" };
|
|
20270
|
+
}
|
|
20271
|
+
writeFileSync(filePath, `${MARKER}
|
|
20257
20272
|
${MEMLY_INSTRUCTION_BLOCK}
|
|
20258
20273
|
${MARKER}
|
|
20259
20274
|
`);
|
|
20260
|
-
|
|
20275
|
+
return { ide: "VS Code / GitHub Copilot", file: filePath, status: "created" };
|
|
20276
|
+
} catch (err) {
|
|
20277
|
+
return {
|
|
20278
|
+
ide: "VS Code / GitHub Copilot",
|
|
20279
|
+
file: filePath,
|
|
20280
|
+
status: "failed",
|
|
20281
|
+
error: err instanceof Error ? err.message : String(err)
|
|
20282
|
+
};
|
|
20283
|
+
}
|
|
20261
20284
|
}
|
|
20262
20285
|
function writeCursorRules(projectRoot) {
|
|
20263
20286
|
const filePath = join(projectRoot, ".cursorrules");
|
|
20264
20287
|
const MARKER = "# memly-instructions";
|
|
20265
|
-
|
|
20266
|
-
|
|
20267
|
-
|
|
20268
|
-
|
|
20269
|
-
|
|
20270
|
-
|
|
20288
|
+
try {
|
|
20289
|
+
if (existsSync(filePath)) {
|
|
20290
|
+
const existing = readFileSync(filePath, "utf-8");
|
|
20291
|
+
if (existing.includes(MARKER)) {
|
|
20292
|
+
return { ide: "Cursor", file: filePath, status: "skipped" };
|
|
20293
|
+
}
|
|
20294
|
+
writeFileSync(filePath, `${existing.trimEnd()}
|
|
20271
20295
|
|
|
20272
20296
|
${MARKER}
|
|
20273
20297
|
${MEMLY_INSTRUCTION_BLOCK}
|
|
20274
20298
|
`);
|
|
20275
|
-
|
|
20276
|
-
|
|
20277
|
-
|
|
20299
|
+
return { ide: "Cursor", file: filePath, status: "updated" };
|
|
20300
|
+
}
|
|
20301
|
+
writeFileSync(filePath, `${MARKER}
|
|
20278
20302
|
${MEMLY_INSTRUCTION_BLOCK}
|
|
20279
20303
|
`);
|
|
20280
|
-
|
|
20304
|
+
return { ide: "Cursor", file: filePath, status: "created" };
|
|
20305
|
+
} catch (err) {
|
|
20306
|
+
return {
|
|
20307
|
+
ide: "Cursor",
|
|
20308
|
+
file: filePath,
|
|
20309
|
+
status: "failed",
|
|
20310
|
+
error: err instanceof Error ? err.message : String(err)
|
|
20311
|
+
};
|
|
20312
|
+
}
|
|
20281
20313
|
}
|
|
20282
20314
|
function writeWindsurfRules(projectRoot) {
|
|
20283
20315
|
const filePath = join(projectRoot, ".windsurfrules");
|
|
20284
20316
|
const MARKER = "# memly-instructions";
|
|
20285
|
-
|
|
20286
|
-
|
|
20287
|
-
|
|
20288
|
-
|
|
20289
|
-
|
|
20290
|
-
|
|
20317
|
+
try {
|
|
20318
|
+
if (existsSync(filePath)) {
|
|
20319
|
+
const existing = readFileSync(filePath, "utf-8");
|
|
20320
|
+
if (existing.includes(MARKER)) {
|
|
20321
|
+
return { ide: "Windsurf", file: filePath, status: "skipped" };
|
|
20322
|
+
}
|
|
20323
|
+
writeFileSync(filePath, `${existing.trimEnd()}
|
|
20291
20324
|
|
|
20292
20325
|
${MARKER}
|
|
20293
20326
|
${MEMLY_INSTRUCTION_BLOCK}
|
|
20294
20327
|
`);
|
|
20295
|
-
|
|
20328
|
+
return { ide: "Windsurf", file: filePath, status: "updated" };
|
|
20329
|
+
}
|
|
20330
|
+
writeFileSync(filePath, `${MARKER}
|
|
20331
|
+
${MEMLY_INSTRUCTION_BLOCK}
|
|
20332
|
+
`);
|
|
20333
|
+
return { ide: "Windsurf", file: filePath, status: "created" };
|
|
20334
|
+
} catch (err) {
|
|
20335
|
+
return {
|
|
20336
|
+
ide: "Windsurf",
|
|
20337
|
+
file: filePath,
|
|
20338
|
+
status: "failed",
|
|
20339
|
+
error: err instanceof Error ? err.message : String(err)
|
|
20340
|
+
};
|
|
20296
20341
|
}
|
|
20297
|
-
|
|
20342
|
+
}
|
|
20343
|
+
function writeClineConfig(projectRoot) {
|
|
20344
|
+
const filePath = join(projectRoot, ".clinerules");
|
|
20345
|
+
const MARKER = "# memly-instructions";
|
|
20346
|
+
try {
|
|
20347
|
+
if (existsSync(filePath)) {
|
|
20348
|
+
const existing = readFileSync(filePath, "utf-8");
|
|
20349
|
+
if (existing.includes(MARKER)) {
|
|
20350
|
+
return { ide: "Cline (VS Code)", file: filePath, status: "skipped" };
|
|
20351
|
+
}
|
|
20352
|
+
writeFileSync(filePath, `${existing.trimEnd()}
|
|
20353
|
+
|
|
20354
|
+
${MARKER}
|
|
20355
|
+
${MEMLY_INSTRUCTION_BLOCK}
|
|
20356
|
+
`);
|
|
20357
|
+
return { ide: "Cline (VS Code)", file: filePath, status: "updated" };
|
|
20358
|
+
}
|
|
20359
|
+
writeFileSync(filePath, `${MARKER}
|
|
20298
20360
|
${MEMLY_INSTRUCTION_BLOCK}
|
|
20299
20361
|
`);
|
|
20300
|
-
|
|
20362
|
+
return { ide: "Cline (VS Code)", file: filePath, status: "created" };
|
|
20363
|
+
} catch (err) {
|
|
20364
|
+
return {
|
|
20365
|
+
ide: "Cline (VS Code)",
|
|
20366
|
+
file: filePath,
|
|
20367
|
+
status: "failed",
|
|
20368
|
+
error: err instanceof Error ? err.message : String(err)
|
|
20369
|
+
};
|
|
20370
|
+
}
|
|
20371
|
+
}
|
|
20372
|
+
function writeContinueConfig() {
|
|
20373
|
+
const configPath = join(homedir(), ".continue", "config.json");
|
|
20374
|
+
if (!existsSync(configPath))
|
|
20375
|
+
return null;
|
|
20376
|
+
try {
|
|
20377
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
20378
|
+
const config2 = JSON.parse(raw);
|
|
20379
|
+
const mcpServers = config2["mcpServers"] ?? {};
|
|
20380
|
+
if (mcpServers["memly"]) {
|
|
20381
|
+
return { ide: "Continue.dev", file: configPath, status: "skipped" };
|
|
20382
|
+
}
|
|
20383
|
+
mcpServers["memly"] = {
|
|
20384
|
+
command: "npx",
|
|
20385
|
+
args: ["-y", "@memly/mcp-server"],
|
|
20386
|
+
env: { MEMLY_API_KEY: process.env.MEMLY_API_KEY ?? "<your-memly-api-key>" }
|
|
20387
|
+
};
|
|
20388
|
+
config2["mcpServers"] = mcpServers;
|
|
20389
|
+
writeFileSync(configPath, JSON.stringify(config2, null, 2));
|
|
20390
|
+
return { ide: "Continue.dev", file: configPath, status: "updated" };
|
|
20391
|
+
} catch (err) {
|
|
20392
|
+
return {
|
|
20393
|
+
ide: "Continue.dev",
|
|
20394
|
+
file: configPath,
|
|
20395
|
+
status: "failed",
|
|
20396
|
+
error: err instanceof Error ? err.message : String(err)
|
|
20397
|
+
};
|
|
20398
|
+
}
|
|
20399
|
+
}
|
|
20400
|
+
function writeZedConfig() {
|
|
20401
|
+
const os = platform();
|
|
20402
|
+
const configPath = os === "darwin" ? join(homedir(), "Library", "Application Support", "Zed", "settings.json") : join(homedir(), ".config", "zed", "settings.json");
|
|
20403
|
+
if (!existsSync(configPath))
|
|
20404
|
+
return null;
|
|
20405
|
+
try {
|
|
20406
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
20407
|
+
const config2 = JSON.parse(raw);
|
|
20408
|
+
const assistant = config2["assistant"] ?? {};
|
|
20409
|
+
const instructions = assistant["instructions"] ?? "";
|
|
20410
|
+
if (instructions.includes("Memly")) {
|
|
20411
|
+
return { ide: "Zed", file: configPath, status: "skipped" };
|
|
20412
|
+
}
|
|
20413
|
+
assistant["instructions"] = instructions ? `${instructions}
|
|
20414
|
+
|
|
20415
|
+
${MEMLY_INSTRUCTION_BLOCK}` : MEMLY_INSTRUCTION_BLOCK;
|
|
20416
|
+
config2["assistant"] = assistant;
|
|
20417
|
+
writeFileSync(configPath, JSON.stringify(config2, null, 2));
|
|
20418
|
+
return { ide: "Zed", file: configPath, status: "updated" };
|
|
20419
|
+
} catch (err) {
|
|
20420
|
+
return {
|
|
20421
|
+
ide: "Zed",
|
|
20422
|
+
file: configPath,
|
|
20423
|
+
status: "failed",
|
|
20424
|
+
error: err instanceof Error ? err.message : String(err)
|
|
20425
|
+
};
|
|
20426
|
+
}
|
|
20427
|
+
}
|
|
20428
|
+
function writeAiderConfig() {
|
|
20429
|
+
const configPath = join(homedir(), ".aider.conf.yml");
|
|
20430
|
+
if (!existsSync(configPath))
|
|
20431
|
+
return null;
|
|
20432
|
+
try {
|
|
20433
|
+
const existing = readFileSync(configPath, "utf-8");
|
|
20434
|
+
if (existing.includes("memly-instructions")) {
|
|
20435
|
+
return { ide: "Aider (CLI)", file: configPath, status: "skipped" };
|
|
20436
|
+
}
|
|
20437
|
+
const instruction = `
|
|
20438
|
+
# memly-instructions
|
|
20439
|
+
edit-format: |
|
|
20440
|
+
${MEMLY_INSTRUCTION_BLOCK.replace(/\n/g, `
|
|
20441
|
+
`)}
|
|
20442
|
+
`;
|
|
20443
|
+
writeFileSync(configPath, existing + instruction);
|
|
20444
|
+
return { ide: "Aider (CLI)", file: configPath, status: "updated" };
|
|
20445
|
+
} catch (err) {
|
|
20446
|
+
return {
|
|
20447
|
+
ide: "Aider (CLI)",
|
|
20448
|
+
file: configPath,
|
|
20449
|
+
status: "failed",
|
|
20450
|
+
error: err instanceof Error ? err.message : String(err)
|
|
20451
|
+
};
|
|
20452
|
+
}
|
|
20453
|
+
}
|
|
20454
|
+
function writeJetBrains(projectRoot) {
|
|
20455
|
+
const ideaDir = join(projectRoot, ".idea");
|
|
20456
|
+
if (!existsSync(ideaDir))
|
|
20457
|
+
return null;
|
|
20458
|
+
const filePath = join(ideaDir, "mcp.json");
|
|
20459
|
+
try {
|
|
20460
|
+
if (existsSync(filePath)) {
|
|
20461
|
+
const existing = readFileSync(filePath, "utf-8");
|
|
20462
|
+
const config2 = JSON.parse(existing);
|
|
20463
|
+
if (config2.servers?.["memly"]) {
|
|
20464
|
+
return { ide: "JetBrains", file: filePath, status: "skipped" };
|
|
20465
|
+
}
|
|
20466
|
+
config2.servers = config2.servers ?? {};
|
|
20467
|
+
config2.servers["memly"] = {
|
|
20468
|
+
command: "npx",
|
|
20469
|
+
args: ["-y", "@memly/mcp-server"],
|
|
20470
|
+
env: { MEMLY_API_KEY: process.env.MEMLY_API_KEY ?? "<your-memly-api-key>" }
|
|
20471
|
+
};
|
|
20472
|
+
writeFileSync(filePath, JSON.stringify(config2, null, 2));
|
|
20473
|
+
return { ide: "JetBrains", file: filePath, status: "updated" };
|
|
20474
|
+
}
|
|
20475
|
+
mkdirSync(ideaDir, { recursive: true });
|
|
20476
|
+
writeFileSync(filePath, JSON.stringify({
|
|
20477
|
+
servers: {
|
|
20478
|
+
memly: {
|
|
20479
|
+
command: "npx",
|
|
20480
|
+
args: ["-y", "@memly/mcp-server"],
|
|
20481
|
+
env: { MEMLY_API_KEY: process.env.MEMLY_API_KEY ?? "<your-memly-api-key>" }
|
|
20482
|
+
}
|
|
20483
|
+
}
|
|
20484
|
+
}, null, 2));
|
|
20485
|
+
return { ide: "JetBrains", file: filePath, status: "created" };
|
|
20486
|
+
} catch (err) {
|
|
20487
|
+
return {
|
|
20488
|
+
ide: "JetBrains",
|
|
20489
|
+
file: filePath,
|
|
20490
|
+
status: "failed",
|
|
20491
|
+
error: err instanceof Error ? err.message : String(err)
|
|
20492
|
+
};
|
|
20493
|
+
}
|
|
20301
20494
|
}
|
|
20302
20495
|
function writeClaudeDesktop() {
|
|
20303
20496
|
const os = platform();
|
|
@@ -20323,56 +20516,140 @@ function writeClaudeDesktop() {
|
|
|
20323
20516
|
servers["memly"]["systemPrompt"] = MEMLY_INSTRUCTION_BLOCK;
|
|
20324
20517
|
writeFileSync(configPath, JSON.stringify(config2, null, 2));
|
|
20325
20518
|
return { ide: "Claude Desktop", file: configPath, status: "updated" };
|
|
20326
|
-
} catch {
|
|
20327
|
-
return
|
|
20519
|
+
} catch (err) {
|
|
20520
|
+
return {
|
|
20521
|
+
ide: "Claude Desktop",
|
|
20522
|
+
file: configPath,
|
|
20523
|
+
status: "failed",
|
|
20524
|
+
error: err instanceof Error ? err.message : String(err)
|
|
20525
|
+
};
|
|
20328
20526
|
}
|
|
20329
20527
|
}
|
|
20330
|
-
function
|
|
20528
|
+
async function reportTelemetry(results) {
|
|
20529
|
+
try {
|
|
20530
|
+
const controller = new AbortController;
|
|
20531
|
+
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
20532
|
+
await fetch("https://api.memly.site/api/telemetry/init", {
|
|
20533
|
+
method: "POST",
|
|
20534
|
+
headers: { "Content-Type": "application/json" },
|
|
20535
|
+
body: JSON.stringify({
|
|
20536
|
+
ides: results.map((r) => r.ide),
|
|
20537
|
+
os: platform(),
|
|
20538
|
+
timestamp: new Date().toISOString()
|
|
20539
|
+
}),
|
|
20540
|
+
signal: controller.signal
|
|
20541
|
+
});
|
|
20542
|
+
clearTimeout(timeout);
|
|
20543
|
+
} catch {}
|
|
20544
|
+
}
|
|
20545
|
+
async function runInit() {
|
|
20331
20546
|
const projectRoot = resolve(process.cwd());
|
|
20332
|
-
|
|
20547
|
+
if (projectRoot === homedir()) {
|
|
20548
|
+
console.error("Error: Running in home directory.");
|
|
20549
|
+
console.error(`Run this command inside your project folder.
|
|
20550
|
+
`);
|
|
20551
|
+
process.exit(1);
|
|
20552
|
+
}
|
|
20333
20553
|
console.log(`
|
|
20334
|
-
|
|
20554
|
+
â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”`);
|
|
20555
|
+
console.log(" Memly Init — Auto-Memory Setup");
|
|
20556
|
+
console.log(`â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”
|
|
20557
|
+
`);
|
|
20558
|
+
const apiKey = process.env.MEMLY_API_KEY;
|
|
20559
|
+
if (!apiKey) {
|
|
20560
|
+
console.error(`Error: MEMLY_API_KEY not set.
|
|
20561
|
+
`);
|
|
20562
|
+
console.error("Get your API key at: https://memly.site/dashboard");
|
|
20563
|
+
console.error(`Then run: export MEMLY_API_KEY=memly_...
|
|
20564
|
+
`);
|
|
20565
|
+
process.exit(1);
|
|
20566
|
+
}
|
|
20567
|
+
process.stdout.write("Validating API key... ");
|
|
20568
|
+
const valid = await validateApiKey(apiKey);
|
|
20569
|
+
if (!valid) {
|
|
20570
|
+
console.error(`✗ Invalid
|
|
20335
20571
|
`);
|
|
20336
|
-
|
|
20572
|
+
console.error(`Check your API key at: https://memly.site/dashboard
|
|
20337
20573
|
`);
|
|
20574
|
+
process.exit(1);
|
|
20575
|
+
}
|
|
20576
|
+
console.log(`✓ Valid
|
|
20577
|
+
`);
|
|
20578
|
+
console.log(`Project: ${projectRoot}
|
|
20579
|
+
`);
|
|
20580
|
+
console.log(`Detecting IDEs...
|
|
20581
|
+
`);
|
|
20582
|
+
const results = [];
|
|
20338
20583
|
results.push(writeCopilotInstructions(projectRoot));
|
|
20339
20584
|
const hasCursor = existsSync(join(projectRoot, ".cursor")) || existsSync(join(projectRoot, ".cursorrules")) || existsSync(join(homedir(), ".cursor"));
|
|
20340
|
-
if (hasCursor)
|
|
20585
|
+
if (hasCursor)
|
|
20341
20586
|
results.push(writeCursorRules(projectRoot));
|
|
20342
|
-
}
|
|
20343
20587
|
const hasWindsurf = existsSync(join(projectRoot, ".windsurfrules")) || existsSync(join(homedir(), ".codeium"));
|
|
20344
|
-
if (hasWindsurf)
|
|
20588
|
+
if (hasWindsurf)
|
|
20345
20589
|
results.push(writeWindsurfRules(projectRoot));
|
|
20346
|
-
|
|
20590
|
+
const clineExtDir = join(homedir(), ".vscode", "extensions");
|
|
20591
|
+
const hasCline = existsSync(join(projectRoot, ".clinerules")) || existsSync(clineExtDir) && readdirSync(clineExtDir).some((d) => d.startsWith("saoudrizwan.claude-dev"));
|
|
20592
|
+
if (hasCline)
|
|
20593
|
+
results.push(writeClineConfig(projectRoot));
|
|
20594
|
+
const continueResult = writeContinueConfig();
|
|
20595
|
+
if (continueResult)
|
|
20596
|
+
results.push(continueResult);
|
|
20597
|
+
const zedResult = writeZedConfig();
|
|
20598
|
+
if (zedResult)
|
|
20599
|
+
results.push(zedResult);
|
|
20600
|
+
const aiderResult = writeAiderConfig();
|
|
20601
|
+
if (aiderResult)
|
|
20602
|
+
results.push(aiderResult);
|
|
20347
20603
|
const claudeResult = writeClaudeDesktop();
|
|
20348
20604
|
if (claudeResult)
|
|
20349
20605
|
results.push(claudeResult);
|
|
20350
|
-
const
|
|
20351
|
-
|
|
20352
|
-
|
|
20353
|
-
|
|
20354
|
-
|
|
20606
|
+
const jetbrainsResult = writeJetBrains(projectRoot);
|
|
20607
|
+
if (jetbrainsResult)
|
|
20608
|
+
results.push(jetbrainsResult);
|
|
20609
|
+
const icons = { created: "✓", updated: "✓", skipped: "⊘", failed: "✗" };
|
|
20610
|
+
console.log(`Results:
|
|
20611
|
+
`);
|
|
20612
|
+
console.log(" IDE Status File");
|
|
20613
|
+
console.log(" ──────────────────────────────────────────────────────────────────");
|
|
20355
20614
|
for (const r of results) {
|
|
20356
20615
|
const rel = r.file.replace(projectRoot, ".").replace(homedir(), "~");
|
|
20357
|
-
|
|
20616
|
+
const statusLabel = r.status === "failed" ? `failed (${r.error})` : r.status;
|
|
20617
|
+
console.log(` ${icons[r.status]} ${r.ide.padEnd(28)} ${r.status.padEnd(8)} ${rel}`);
|
|
20618
|
+
if (r.status === "failed")
|
|
20619
|
+
console.log(` └─ ${statusLabel}`);
|
|
20358
20620
|
}
|
|
20359
20621
|
if (results.length === 0) {
|
|
20360
|
-
console.log(
|
|
20361
|
-
|
|
20622
|
+
console.log(`
|
|
20623
|
+
No supported IDEs detected.`);
|
|
20624
|
+
console.log(` Add this block manually to your IDE config:
|
|
20362
20625
|
`);
|
|
20363
20626
|
console.log(MEMLY_INSTRUCTION_BLOCK);
|
|
20627
|
+
console.log(`
|
|
20628
|
+
`);
|
|
20629
|
+
return;
|
|
20364
20630
|
}
|
|
20631
|
+
try {
|
|
20632
|
+
writeFileSync(join(projectRoot, ".memly-initialized"), new Date().toISOString());
|
|
20633
|
+
} catch {}
|
|
20634
|
+
await reportTelemetry(results);
|
|
20365
20635
|
console.log(`
|
|
20366
|
-
|
|
20636
|
+
â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”
|
|
20637
|
+
`);
|
|
20638
|
+
console.log(`Setup complete. Next steps:
|
|
20639
|
+
`);
|
|
20640
|
+
console.log(" 1. Restart your IDE");
|
|
20641
|
+
console.log(" 2. Start a new conversation");
|
|
20642
|
+
console.log(` 3. Memly loads context automatically
|
|
20367
20643
|
`);
|
|
20368
|
-
console.log(
|
|
20644
|
+
console.log("Dashboard: https://memly.site/dashboard");
|
|
20645
|
+
console.log(`Docs: https://memly.site/docs
|
|
20369
20646
|
`);
|
|
20370
20647
|
}
|
|
20371
20648
|
|
|
20372
20649
|
// src/index.ts
|
|
20373
20650
|
var apiKey = process.env.MEMLY_API_KEY;
|
|
20374
20651
|
if (process.argv.includes("--init") || process.argv.includes("init")) {
|
|
20375
|
-
runInit();
|
|
20652
|
+
await runInit();
|
|
20376
20653
|
process.exit(0);
|
|
20377
20654
|
}
|
|
20378
20655
|
if (!apiKey) {
|