@cachly-dev/mcp-server 0.1.0 → 0.2.0
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/CHANGELOG.md +22 -2
- package/README.md +48 -65
- package/dist/index.js +196 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +213 -65
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
# Changelog – cachly SDK (mcp)
|
|
2
2
|
|
|
3
3
|
**Language:** MCP (Model Context Protocol)
|
|
4
|
-
**Package:** `@cachly-dev/mcp` on **npm**
|
|
4
|
+
**Package:** `@cachly-dev/mcp-server` on **npm**
|
|
5
5
|
|
|
6
6
|
> Full cross-SDK release notes: [../CHANGELOG.md](../CHANGELOG.md)
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
## [0.2.0] – 2026-04-
|
|
10
|
+
## [0.2.0] – 2026-04-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`setup` command** — interactive zero-arg CLI wizard (`npx @cachly-dev/mcp-server setup`):
|
|
15
|
+
- Reads `CACHLY_JWT` from env or prompts interactively via readline
|
|
16
|
+
- Fetches instances from API; presents list when multiple exist
|
|
17
|
+
- Auto-detects installed editors (Cursor, Windsurf, VS Code, Continue.dev) by checking for their config directories
|
|
18
|
+
- Writes the correct MCP config file for each detected editor in one step
|
|
19
|
+
- Always writes/updates `CLAUDE.md` (idempotent via `<!-- cachly-brain-start/end -->` markers)
|
|
20
|
+
- **`init` command** now idempotent — re-running `npx @cachly-dev/mcp-server init` updates the brain block in `CLAUDE.md` instead of appending a duplicate
|
|
21
|
+
- Shared helpers: `buildMcpConfig()`, `buildClaudeMdBlock()`, `writeClaudeMd()` — used by both `setup` and `init`
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- `init` no longer duplicates the brain block in `CLAUDE.md` when run multiple times
|
|
26
|
+
- Correct package name `@cachly-dev/mcp-server` used consistently (was `@cachly-dev/mcp` in generated configs)
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## [0.1.1] – 2026-04-07
|
|
11
31
|
|
|
12
32
|
### Fixed
|
|
13
33
|
|
package/README.md
CHANGED
|
@@ -2,49 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
> Manage your [cachly.dev](https://cachly.dev) cache instances directly from **GitHub Copilot, Claude, Cursor, Windsurf** and any other MCP-compatible AI assistant.
|
|
4
4
|
|
|
5
|
-
## 🚀
|
|
5
|
+
## 🚀 Zero-Touch Setup — One Command
|
|
6
6
|
|
|
7
|
-
Stop your AI from re-reading your entire codebase every time. One command enables **context memory
|
|
7
|
+
Stop your AI from re-reading your entire codebase every time. One command enables **context memory** and configures all your editors automatically:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
11
|
-
# 2. Copy your JWT token from Settings
|
|
12
|
-
# 3. Add to .vscode/mcp.json:
|
|
10
|
+
CACHLY_JWT=your-jwt npx @cachly-dev/mcp-server setup
|
|
13
11
|
```
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"args": ["-y", "@cachly-dev/mcp-server"],
|
|
22
|
-
"env": {
|
|
23
|
-
"CACHLY_JWT": "your-jwt-token-here"
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
```
|
|
13
|
+
The interactive wizard will:
|
|
14
|
+
1. Authenticate with your cachly account (or prompt for JWT)
|
|
15
|
+
2. Let you pick which cache instance to use as your AI Brain
|
|
16
|
+
3. **Auto-detect** Cursor, Windsurf, VS Code, Claude Code, and Continue.dev
|
|
17
|
+
4. Write the correct MCP config for every detected editor
|
|
18
|
+
5. Create/update `CLAUDE.md` (idempotent — safe to re-run)
|
|
29
19
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
```markdown
|
|
33
|
-
## Cachly AI Brain
|
|
34
|
-
|
|
35
|
-
At the START of every session — single call:
|
|
36
|
-
session_start(instance_id = "<your-instance-id>", focus = "<what you're working on>")
|
|
20
|
+
**Result:** 60% fewer file reads, instant context across sessions, zero re-discovery.
|
|
37
21
|
|
|
38
|
-
|
|
39
|
-
session_end(instance_id = "<your-instance-id>", summary = "...", files_changed = [...])
|
|
22
|
+
### Non-interactive (CI / scripted setup)
|
|
40
23
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
24
|
+
```bash
|
|
25
|
+
CACHLY_JWT=your-jwt npx @cachly-dev/mcp-server init \
|
|
26
|
+
--instance-id your-instance-id \
|
|
27
|
+
--editor vscode
|
|
44
28
|
```
|
|
45
29
|
|
|
46
|
-
**Result:** 60% fewer file reads, instant context across sessions, zero re-discovery.
|
|
47
|
-
|
|
48
30
|
## What you can do
|
|
49
31
|
|
|
50
32
|
Once connected, just talk to your AI assistant:
|
|
@@ -105,73 +87,74 @@ Once connected, just talk to your AI assistant:
|
|
|
105
87
|
|
|
106
88
|
## Setup
|
|
107
89
|
|
|
108
|
-
###
|
|
90
|
+
### Recommended: Zero-Touch via npx
|
|
109
91
|
|
|
110
92
|
```bash
|
|
111
|
-
|
|
112
|
-
npm run build
|
|
93
|
+
CACHLY_JWT=your-jwt npx @cachly-dev/mcp-server setup
|
|
113
94
|
```
|
|
114
95
|
|
|
115
|
-
|
|
96
|
+
No install, no build step. The wizard auto-detects your editors and writes all config files.
|
|
116
97
|
|
|
117
|
-
|
|
98
|
+
### Manual configuration
|
|
118
99
|
|
|
119
|
-
|
|
100
|
+
Get your JWT token at **[cachly.dev/settings](https://cachly.dev/settings)** → API Tokens.
|
|
120
101
|
|
|
121
|
-
####
|
|
122
|
-
|
|
123
|
-
Add to `.vscode/mcp.json` in your project (or global user settings):
|
|
102
|
+
#### Claude Code / Claude Desktop
|
|
124
103
|
|
|
125
104
|
```json
|
|
126
105
|
{
|
|
127
|
-
"
|
|
106
|
+
"mcpServers": {
|
|
128
107
|
"cachly": {
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"env": {
|
|
133
|
-
"CACHLY_JWT": "your-jwt-token-here",
|
|
134
|
-
"CACHLY_API_URL": "https://api.cachly.dev"
|
|
135
|
-
}
|
|
108
|
+
"command": "npx",
|
|
109
|
+
"args": ["-y", "@cachly-dev/mcp-server"],
|
|
110
|
+
"env": { "CACHLY_JWT": "your-jwt-token-here" }
|
|
136
111
|
}
|
|
137
112
|
}
|
|
138
113
|
}
|
|
139
114
|
```
|
|
140
115
|
|
|
141
|
-
|
|
116
|
+
- **Claude Code:** add to `.claude/mcp.json` in your project
|
|
117
|
+
- **Claude Desktop (macOS):** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
142
118
|
|
|
143
|
-
####
|
|
119
|
+
#### GitHub Copilot (VS Code)
|
|
144
120
|
|
|
145
|
-
Add to
|
|
121
|
+
Add to `.vscode/mcp.json`:
|
|
146
122
|
|
|
147
123
|
```json
|
|
148
124
|
{
|
|
149
|
-
"
|
|
125
|
+
"servers": {
|
|
150
126
|
"cachly": {
|
|
151
|
-
"
|
|
152
|
-
"
|
|
153
|
-
"
|
|
154
|
-
|
|
155
|
-
}
|
|
127
|
+
"type": "stdio",
|
|
128
|
+
"command": "npx",
|
|
129
|
+
"args": ["-y", "@cachly-dev/mcp-server"],
|
|
130
|
+
"env": { "CACHLY_JWT": "your-jwt-token-here" }
|
|
156
131
|
}
|
|
157
132
|
}
|
|
158
133
|
}
|
|
159
134
|
```
|
|
160
135
|
|
|
161
|
-
|
|
136
|
+
Then: **Ctrl/Cmd+Shift+P → "MCP: List Servers"** → start `cachly`.
|
|
162
137
|
|
|
163
|
-
|
|
138
|
+
#### Cursor
|
|
164
139
|
|
|
165
|
-
|
|
140
|
+
Add to `.cursor/mcp.json`:
|
|
166
141
|
|
|
167
142
|
```json
|
|
168
143
|
{
|
|
169
|
-
"
|
|
170
|
-
|
|
171
|
-
|
|
144
|
+
"mcpServers": {
|
|
145
|
+
"cachly": {
|
|
146
|
+
"command": "npx",
|
|
147
|
+
"args": ["-y", "@cachly-dev/mcp-server"],
|
|
148
|
+
"env": { "CACHLY_JWT": "your-jwt-token-here" }
|
|
149
|
+
}
|
|
150
|
+
}
|
|
172
151
|
}
|
|
173
152
|
```
|
|
174
153
|
|
|
154
|
+
#### Windsurf / Continue.dev
|
|
155
|
+
|
|
156
|
+
Same `stdio`/`mcpServers` format — add to their respective MCP config file.
|
|
157
|
+
|
|
175
158
|
## Environment Variables
|
|
176
159
|
|
|
177
160
|
| Variable | Required | Default | Description |
|
package/dist/index.js
CHANGED
|
@@ -2790,87 +2790,221 @@ process.on('unhandledRejection', (reason) => {
|
|
|
2790
2790
|
// Write to stderr only (stdout is the JSON-RPC channel)
|
|
2791
2791
|
process.stderr.write(`[cachly-mcp] unhandledRejection: ${msg}\n`);
|
|
2792
2792
|
});
|
|
2793
|
+
// ── CLI helpers ───────────────────────────────────────────────────────────────
|
|
2794
|
+
const EDITOR_FILES = {
|
|
2795
|
+
claude: '.mcp.json',
|
|
2796
|
+
cursor: '.cursor/mcp.json',
|
|
2797
|
+
windsurf: '.windsurf/mcp.json',
|
|
2798
|
+
copilot: '.vscode/mcp.json',
|
|
2799
|
+
continue: '.continue/config.json',
|
|
2800
|
+
};
|
|
2801
|
+
const CLAUDE_MD_MARKER_START = '<!-- cachly-brain-start -->';
|
|
2802
|
+
const CLAUDE_MD_MARKER_END = '<!-- cachly-brain-end -->';
|
|
2803
|
+
function buildMcpConfig(apiKey, instanceId, editor) {
|
|
2804
|
+
if (editor === 'continue') {
|
|
2805
|
+
return JSON.stringify({
|
|
2806
|
+
experimental: {
|
|
2807
|
+
modelContextProtocolServers: [{
|
|
2808
|
+
transport: { type: 'stdio', command: 'npx', args: ['-y', '@cachly-dev/mcp-server@latest'] },
|
|
2809
|
+
env: { CACHLY_API_URL: 'https://api.cachly.dev', CACHLY_JWT: apiKey, CACHLY_BRAIN_INSTANCE_ID: instanceId },
|
|
2810
|
+
}],
|
|
2811
|
+
},
|
|
2812
|
+
}, null, 2);
|
|
2813
|
+
}
|
|
2814
|
+
return JSON.stringify({
|
|
2815
|
+
mcpServers: {
|
|
2816
|
+
cachly: {
|
|
2817
|
+
command: 'npx',
|
|
2818
|
+
args: ['-y', '@cachly-dev/mcp-server@latest'],
|
|
2819
|
+
env: { CACHLY_API_URL: 'https://api.cachly.dev', CACHLY_JWT: apiKey, CACHLY_BRAIN_INSTANCE_ID: instanceId },
|
|
2820
|
+
},
|
|
2821
|
+
},
|
|
2822
|
+
}, null, 2);
|
|
2823
|
+
}
|
|
2824
|
+
function buildClaudeMdBlock(instanceId) {
|
|
2825
|
+
return `${CLAUDE_MD_MARKER_START}
|
|
2826
|
+
## Cachly AI Brain
|
|
2827
|
+
|
|
2828
|
+
The Cachly MCP server is available as tool \`cachly\`.
|
|
2829
|
+
|
|
2830
|
+
### At the START of every session (one call):
|
|
2831
|
+
\`\`\`
|
|
2832
|
+
session_start(
|
|
2833
|
+
instance_id = "${instanceId}",
|
|
2834
|
+
focus = "<what you plan to work on today>",
|
|
2835
|
+
)
|
|
2836
|
+
\`\`\`
|
|
2837
|
+
|
|
2838
|
+
### At the END of every session:
|
|
2839
|
+
\`\`\`
|
|
2840
|
+
session_end(
|
|
2841
|
+
instance_id = "${instanceId}",
|
|
2842
|
+
summary = "What was accomplished (2-3 sentences)",
|
|
2843
|
+
files_changed = ["src/..."],
|
|
2844
|
+
lessons_learned = 1,
|
|
2845
|
+
)
|
|
2846
|
+
\`\`\`
|
|
2847
|
+
|
|
2848
|
+
### After fixing any bug or learning something new:
|
|
2849
|
+
\`\`\`
|
|
2850
|
+
learn_from_attempts(
|
|
2851
|
+
instance_id = "${instanceId}",
|
|
2852
|
+
topic = "category:keyword",
|
|
2853
|
+
outcome = "success",
|
|
2854
|
+
what_worked = "...",
|
|
2855
|
+
what_failed = "...",
|
|
2856
|
+
severity = "critical" | "major" | "minor",
|
|
2857
|
+
file_paths = ["path/to/file"],
|
|
2858
|
+
commands = ["the command that worked"],
|
|
2859
|
+
tags = ["tag1", "tag2"],
|
|
2860
|
+
)
|
|
2861
|
+
\`\`\`
|
|
2862
|
+
${CLAUDE_MD_MARKER_END}`;
|
|
2863
|
+
}
|
|
2864
|
+
async function writeClaudeMd(projectDir, instanceId) {
|
|
2865
|
+
const { writeFile, appendFile, readFile } = await import('node:fs/promises');
|
|
2866
|
+
const { existsSync } = await import('node:fs');
|
|
2867
|
+
const { resolve } = await import('node:path');
|
|
2868
|
+
const claudeMdPath = resolve(projectDir, 'CLAUDE.md');
|
|
2869
|
+
const block = '\n' + buildClaudeMdBlock(instanceId) + '\n';
|
|
2870
|
+
if (existsSync(claudeMdPath)) {
|
|
2871
|
+
const existing = await readFile(claudeMdPath, 'utf-8');
|
|
2872
|
+
if (existing.includes(CLAUDE_MD_MARKER_START)) {
|
|
2873
|
+
// Idempotent update: replace existing block (new instance-id, refreshed content)
|
|
2874
|
+
const updated = existing.replace(new RegExp(`${CLAUDE_MD_MARKER_START}[\\s\\S]*?${CLAUDE_MD_MARKER_END}`), buildClaudeMdBlock(instanceId));
|
|
2875
|
+
await writeFile(claudeMdPath, updated, 'utf-8');
|
|
2876
|
+
return 'updated';
|
|
2877
|
+
}
|
|
2878
|
+
await appendFile(claudeMdPath, block, 'utf-8');
|
|
2879
|
+
return 'appended';
|
|
2880
|
+
}
|
|
2881
|
+
await writeFile(claudeMdPath, block.trimStart(), 'utf-8');
|
|
2882
|
+
return 'written';
|
|
2883
|
+
}
|
|
2793
2884
|
// ── CLI: cachly init ──────────────────────────────────────────────────────────
|
|
2794
2885
|
// Usage: npx @cachly-dev/mcp-server init --instance-id <id> --api-key <key> [--editor claude|cursor|windsurf|copilot|continue] [--project-dir /path]
|
|
2795
2886
|
if (process.argv[2] === 'init') {
|
|
2796
2887
|
const { writeFile, mkdir } = await import('node:fs/promises');
|
|
2797
|
-
const { existsSync } = await import('node:fs');
|
|
2798
2888
|
const { resolve, dirname } = await import('node:path');
|
|
2799
|
-
// Parse CLI flags
|
|
2800
2889
|
const argv = process.argv.slice(3);
|
|
2801
|
-
const flag = (name) => {
|
|
2802
|
-
const i = argv.indexOf(`--${name}`);
|
|
2803
|
-
return i !== -1 ? argv[i + 1] : undefined;
|
|
2804
|
-
};
|
|
2890
|
+
const flag = (name) => { const i = argv.indexOf(`--${name}`); return i !== -1 ? argv[i + 1] : undefined; };
|
|
2805
2891
|
const instanceId = flag('instance-id') ?? process.env.CACHLY_BRAIN_INSTANCE_ID;
|
|
2806
2892
|
const apiKey = flag('api-key') ?? process.env.CACHLY_JWT;
|
|
2807
2893
|
const editor = (flag('editor') ?? 'claude').toLowerCase();
|
|
2808
2894
|
const projectDir = resolve(flag('project-dir') ?? '.');
|
|
2809
2895
|
if (!instanceId || !apiKey) {
|
|
2810
2896
|
console.error('\nUsage: npx @cachly-dev/mcp-server@latest init --instance-id <uuid> --api-key <cky_live_...> [--editor claude|cursor|windsurf|copilot|continue] [--project-dir /path]\n');
|
|
2811
|
-
console.error('
|
|
2897
|
+
console.error('Or run interactively (no flags needed): npx @cachly-dev/mcp-server@latest setup\n');
|
|
2898
|
+
console.error('Get your credentials from: https://cachly.dev/setup-ai\n');
|
|
2812
2899
|
process.exit(1);
|
|
2813
2900
|
}
|
|
2814
|
-
const
|
|
2815
|
-
claude: '.mcp.json',
|
|
2816
|
-
cursor: '.cursor/mcp.json',
|
|
2817
|
-
windsurf: '.windsurf/mcp.json',
|
|
2818
|
-
copilot: '.vscode/mcp.json',
|
|
2819
|
-
continue: '.continue/config.json',
|
|
2820
|
-
};
|
|
2821
|
-
const configFile = editorFiles[editor] ?? '.mcp.json';
|
|
2901
|
+
const configFile = EDITOR_FILES[editor] ?? '.mcp.json';
|
|
2822
2902
|
const configPath = resolve(projectDir, configFile);
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2903
|
+
await mkdir(dirname(configPath), { recursive: true });
|
|
2904
|
+
await writeFile(configPath, buildMcpConfig(apiKey, instanceId, editor), 'utf-8');
|
|
2905
|
+
console.log(`\n✅ Written: ${configFile}`);
|
|
2906
|
+
// Always write CLAUDE.md (idempotent — safe to run multiple times)
|
|
2907
|
+
const result = await writeClaudeMd(projectDir, instanceId);
|
|
2908
|
+
const action = result === 'updated' ? '✅ Updated' : result === 'appended' ? '✅ Appended to' : '✅ Written';
|
|
2909
|
+
console.log(`${action}: CLAUDE.md`);
|
|
2910
|
+
console.log(`\n🧠 Cachly AI Brain configured for ${editor === 'claude' ? 'Claude Code' : editor}!`);
|
|
2911
|
+
console.log(` Restart your editor — the \`cachly\` MCP tools will appear.\n`);
|
|
2912
|
+
process.exit(0);
|
|
2913
|
+
}
|
|
2914
|
+
// ── CLI: cachly setup (interactive — no flags required) ───────────────────────
|
|
2915
|
+
// Usage: npx @cachly-dev/mcp-server setup
|
|
2916
|
+
if (process.argv[2] === 'setup') {
|
|
2917
|
+
const { writeFile, mkdir } = await import('node:fs/promises');
|
|
2918
|
+
const { existsSync } = await import('node:fs');
|
|
2919
|
+
const { resolve, dirname } = await import('node:path');
|
|
2920
|
+
const { createInterface } = await import('node:readline');
|
|
2921
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
2922
|
+
const ask = (q) => new Promise(res => rl.question(q, ans => res(ans.trim())));
|
|
2923
|
+
console.log('\n🧠 cachly AI Brain — Interactive Setup');
|
|
2924
|
+
console.log('────────────────────────────────────────\n');
|
|
2925
|
+
// ── Step 1: API token ──────────────────────────────────────────────────────
|
|
2926
|
+
let token = process.env.CACHLY_JWT ?? '';
|
|
2927
|
+
if (token) {
|
|
2928
|
+
console.log('✓ Using token from CACHLY_JWT env var\n');
|
|
2838
2929
|
}
|
|
2839
2930
|
else {
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
CACHLY_API_URL: 'https://api.cachly.dev',
|
|
2847
|
-
CACHLY_JWT: apiKey,
|
|
2848
|
-
CACHLY_BRAIN_INSTANCE_ID: instanceId,
|
|
2849
|
-
},
|
|
2850
|
-
},
|
|
2851
|
-
},
|
|
2852
|
-
}, null, 2);
|
|
2853
|
-
}
|
|
2854
|
-
// Create parent dirs if needed, then write config
|
|
2855
|
-
await mkdir(dirname(configPath), { recursive: true });
|
|
2856
|
-
await writeFile(configPath, configContent, 'utf-8');
|
|
2857
|
-
console.log(`\n✅ Written: ${configPath}`);
|
|
2858
|
-
// For Claude Code, also offer to append CLAUDE.md snippet
|
|
2859
|
-
if (editor === 'claude') {
|
|
2860
|
-
const claudeMdSnippet = `\n## Cachly AI Brain\n\nThe Cachly MCP server is available as tool \`cachly\`.\n\n### At the START of every session (one call):\n\`\`\`\nsession_start(\n instance_id = "${instanceId}",\n focus = "<what you plan to work on today>",\n)\n\`\`\`\n\n### At the END of every session:\n\`\`\`\nsession_end(\n instance_id = "${instanceId}",\n summary = "What was accomplished",\n files_changed = ["src/..."],\n lessons_learned = 1,\n)\n\`\`\`\n\n### After fixing any bug or learning something:\n\`\`\`\nlearn_from_attempts(\n instance_id = "${instanceId}",\n topic = "category:keyword",\n outcome = "success",\n what_worked = "...",\n what_failed = "...",\n severity = "critical" | "major" | "minor",\n file_paths = ["path/to/file"],\n commands = ["the command that worked"],\n tags = ["tag1", "tag2"],\n)\n\`\`\`\n`;
|
|
2861
|
-
const claudeMdPath = resolve(projectDir, 'CLAUDE.md');
|
|
2862
|
-
const { appendFile } = await import('node:fs/promises');
|
|
2863
|
-
if (existsSync(claudeMdPath)) {
|
|
2864
|
-
await appendFile(claudeMdPath, claudeMdSnippet, 'utf-8');
|
|
2865
|
-
console.log(`✅ Appended: ${claudeMdPath}`);
|
|
2866
|
-
}
|
|
2867
|
-
else {
|
|
2868
|
-
await writeFile(claudeMdPath, claudeMdSnippet.trimStart(), 'utf-8');
|
|
2869
|
-
console.log(`✅ Written: ${claudeMdPath}`);
|
|
2931
|
+
console.log('Step 1: Get your API token at https://cachly.dev/setup-ai\n');
|
|
2932
|
+
token = await ask(' Paste API token (cky_live_...): ');
|
|
2933
|
+
if (!token) {
|
|
2934
|
+
console.error('\nToken is required. Aborting.\n');
|
|
2935
|
+
rl.close();
|
|
2936
|
+
process.exit(1);
|
|
2870
2937
|
}
|
|
2938
|
+
console.log('');
|
|
2871
2939
|
}
|
|
2872
|
-
|
|
2873
|
-
|
|
2940
|
+
// ── Step 2: Fetch & pick instance ─────────────────────────────────────────
|
|
2941
|
+
process.stdout.write('⏳ Fetching your instances...');
|
|
2942
|
+
let instances = [];
|
|
2943
|
+
try {
|
|
2944
|
+
const res = await fetch(`${API_URL}/api/v1/instances`, {
|
|
2945
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
2946
|
+
});
|
|
2947
|
+
if (!res.ok)
|
|
2948
|
+
throw new Error(`HTTP ${res.status}`);
|
|
2949
|
+
const body = await res.json();
|
|
2950
|
+
instances = (body.data ?? []).filter(i => i.status === 'running');
|
|
2951
|
+
}
|
|
2952
|
+
catch (e) {
|
|
2953
|
+
console.error(`\n\nFailed to fetch instances: ${e.message}`);
|
|
2954
|
+
console.error('Check your token and try again.\n');
|
|
2955
|
+
rl.close();
|
|
2956
|
+
process.exit(1);
|
|
2957
|
+
}
|
|
2958
|
+
console.log(` found ${instances.length}\n`);
|
|
2959
|
+
if (instances.length === 0) {
|
|
2960
|
+
console.error('No running instances found. Create one at https://cachly.dev/instances\n');
|
|
2961
|
+
rl.close();
|
|
2962
|
+
process.exit(1);
|
|
2963
|
+
}
|
|
2964
|
+
let instance = instances[0];
|
|
2965
|
+
if (instances.length > 1) {
|
|
2966
|
+
console.log('Step 2: Pick your Brain instance:\n');
|
|
2967
|
+
instances.forEach((inst, idx) => console.log(` ${idx + 1}. ${inst.name.padEnd(24)} ${inst.tier.padEnd(10)} ${inst.region}`));
|
|
2968
|
+
const raw = await ask(`\n Pick [1-${instances.length}] (Enter = 1): `);
|
|
2969
|
+
const idx = parseInt(raw || '1', 10) - 1;
|
|
2970
|
+
instance = instances[Math.max(0, Math.min(instances.length - 1, idx))];
|
|
2971
|
+
}
|
|
2972
|
+
console.log(`✓ Instance: ${instance.name} (${instance.id.slice(0, 8)}…)\n`);
|
|
2973
|
+
// ── Step 3: Detect editors ────────────────────────────────────────────────
|
|
2974
|
+
const cwd = process.cwd();
|
|
2975
|
+
const detected = [];
|
|
2976
|
+
// Claude Code: always include (CLAUDE.md is universal)
|
|
2977
|
+
detected.push('claude');
|
|
2978
|
+
if (existsSync(resolve(cwd, '.cursor')))
|
|
2979
|
+
detected.push('cursor');
|
|
2980
|
+
if (existsSync(resolve(cwd, '.windsurf')))
|
|
2981
|
+
detected.push('windsurf');
|
|
2982
|
+
if (existsSync(resolve(cwd, '.vscode')))
|
|
2983
|
+
detected.push('copilot');
|
|
2984
|
+
if (existsSync(resolve(cwd, '.continue')))
|
|
2985
|
+
detected.push('continue');
|
|
2986
|
+
const editorLabel = (e) => ({ claude: 'Claude Code', cursor: 'Cursor', windsurf: 'Windsurf', copilot: 'GitHub Copilot', continue: 'Continue.dev' })[e] ?? e;
|
|
2987
|
+
console.log(`Step 3: Detected editors: ${detected.map(editorLabel).join(', ')}`);
|
|
2988
|
+
const editorsRaw = await ask(` Configure for which? [all / ${detected.join('/')}] (Enter = all): `);
|
|
2989
|
+
const editorsToSetup = !editorsRaw || editorsRaw.toLowerCase() === 'all'
|
|
2990
|
+
? detected
|
|
2991
|
+
: editorsRaw.split(/[,\s]+/).map(s => s.toLowerCase()).filter(s => EDITOR_FILES[s]);
|
|
2992
|
+
console.log('');
|
|
2993
|
+
// ── Step 4: Write editor configs ──────────────────────────────────────────
|
|
2994
|
+
for (const editor of editorsToSetup) {
|
|
2995
|
+
const configFile = EDITOR_FILES[editor] ?? '.mcp.json';
|
|
2996
|
+
const configPath = resolve(cwd, configFile);
|
|
2997
|
+
await mkdir(dirname(configPath), { recursive: true });
|
|
2998
|
+
await writeFile(configPath, buildMcpConfig(token, instance.id, editor), 'utf-8');
|
|
2999
|
+
console.log(`✅ Written: ${configFile}`);
|
|
3000
|
+
}
|
|
3001
|
+
// ── Step 5: CLAUDE.md (always — idempotent) ───────────────────────────────
|
|
3002
|
+
const mdResult = await writeClaudeMd(cwd, instance.id);
|
|
3003
|
+
const mdLabel = mdResult === 'updated' ? '✅ Updated' : mdResult === 'appended' ? '✅ Appended to' : '✅ Written';
|
|
3004
|
+
console.log(`${mdLabel}: CLAUDE.md`);
|
|
3005
|
+
rl.close();
|
|
3006
|
+
console.log(`\n🧠 Done! Restart your editor — the \`cachly\` MCP tools will appear.`);
|
|
3007
|
+
console.log(` Your AI now has persistent memory across every session.\n`);
|
|
2874
3008
|
process.exit(0);
|
|
2875
3009
|
}
|
|
2876
3010
|
// ── Start ─────────────────────────────────────────────────────────────────────
|