agentcache 0.1.3 → 0.1.4
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 +16 -8
- package/dist/chunk-MKQKD2PY.js +273 -0
- package/dist/cli.js +1 -1
- package/dist/postinstall.js +1 -1
- package/dist/{setup-C6PI6YD7.js → setup-YHD2FO4N.js} +1 -1
- package/package.json +1 -1
- package/dist/chunk-7FIT3LNA.js +0 -207
package/README.md
CHANGED
|
@@ -133,17 +133,25 @@ AgentCache prevents knowledge from growing unbounded:
|
|
|
133
133
|
|
|
134
134
|
## Supported IDEs
|
|
135
135
|
|
|
136
|
-
| IDE | MCP | Transcript Recovery | Hooks |
|
|
137
|
-
|
|
138
|
-
| Claude Code | Yes | Full (JSONL) | Stop, SessionStart, PreToolUse |
|
|
139
|
-
| Cursor | Yes | Incremental only | — |
|
|
140
|
-
| Roo Code | Yes | Incremental only | — |
|
|
141
|
-
| Windsurf | Yes | Incremental only | — |
|
|
142
|
-
| Continue | Yes | Full (JSON) | — |
|
|
143
|
-
| Codex | Yes | Incremental only | — |
|
|
136
|
+
| IDE | MCP | Auto-Approve | Transcript Recovery | Hooks |
|
|
137
|
+
|-----|-----|-------------|--------------------|----|
|
|
138
|
+
| Claude Code | Yes | Yes (automatic) | Full (JSONL) | Stop, SessionStart, PreToolUse |
|
|
139
|
+
| Cursor | Yes | **Manual** (see below) | Incremental only | — |
|
|
140
|
+
| Roo Code | Yes | Yes (automatic) | Incremental only | — |
|
|
141
|
+
| Windsurf | Yes | Yes (automatic) | Incremental only | — |
|
|
142
|
+
| Continue | Yes | Yes (automatic) | Full (JSON) | — |
|
|
143
|
+
| Codex | Yes | Yes (automatic) | Incremental only | — |
|
|
144
144
|
|
|
145
145
|
"Incremental only" means if the agent submits observations during the session, they're saved. If the session terminates before any submission, those observations are lost (no transcript access).
|
|
146
146
|
|
|
147
|
+
### Cursor: Enable Auto-Approve
|
|
148
|
+
|
|
149
|
+
Cursor does not support programmatic auto-approve for MCP tools. After installing, you need to manually enable it once:
|
|
150
|
+
|
|
151
|
+
1. Open Cursor Settings → MCP
|
|
152
|
+
2. Find **agentcache** in the server list
|
|
153
|
+
3. Set tool approval to **"Always allow"** (or enable "Yolo mode" in Cursor settings for all tools)
|
|
154
|
+
|
|
147
155
|
## Data Storage
|
|
148
156
|
|
|
149
157
|
All data lives in `~/.loop/loop.db` (SQLite with WAL mode for concurrent access).
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// src/utils/ide-detector.ts
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
function getRooConfigPath() {
|
|
6
|
+
const home = homedir();
|
|
7
|
+
if (process.platform === "darwin") {
|
|
8
|
+
return join(home, "Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json");
|
|
9
|
+
}
|
|
10
|
+
if (process.platform === "win32") {
|
|
11
|
+
return join(process.env.APPDATA || join(home, "AppData/Roaming"), "Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json");
|
|
12
|
+
}
|
|
13
|
+
return join(home, ".config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json");
|
|
14
|
+
}
|
|
15
|
+
function getWindsurfConfigPath() {
|
|
16
|
+
const home = homedir();
|
|
17
|
+
return join(home, ".codeium", "windsurf", "mcp_config.json");
|
|
18
|
+
}
|
|
19
|
+
function getContinueConfigPath() {
|
|
20
|
+
const home = homedir();
|
|
21
|
+
return join(home, ".continue", "mcpServers", "agentcache.json");
|
|
22
|
+
}
|
|
23
|
+
function getCodexConfigPath() {
|
|
24
|
+
const home = homedir();
|
|
25
|
+
return join(home, ".codex", "config.toml");
|
|
26
|
+
}
|
|
27
|
+
function detectInstalledIdes() {
|
|
28
|
+
const home = homedir();
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
name: "Claude Code",
|
|
32
|
+
detected: existsSync(join(home, ".claude")),
|
|
33
|
+
mcpConfigPath: join(home, ".claude.json"),
|
|
34
|
+
mcpConfigFormat: "claude-settings"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "Cursor",
|
|
38
|
+
detected: existsSync(join(home, ".cursor")),
|
|
39
|
+
mcpConfigPath: join(home, ".cursor", "mcp.json"),
|
|
40
|
+
mcpConfigFormat: "mcp-json"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "Roo Code",
|
|
44
|
+
detected: existsSync(getRooConfigPath()),
|
|
45
|
+
mcpConfigPath: getRooConfigPath(),
|
|
46
|
+
mcpConfigFormat: "mcp-json"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "Windsurf",
|
|
50
|
+
detected: existsSync(join(home, ".codeium", "windsurf")) || existsSync(join(home, ".windsurf")),
|
|
51
|
+
mcpConfigPath: getWindsurfConfigPath(),
|
|
52
|
+
mcpConfigFormat: "mcp-json"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "Continue",
|
|
56
|
+
detected: existsSync(join(home, ".continue")),
|
|
57
|
+
mcpConfigPath: getContinueConfigPath(),
|
|
58
|
+
mcpConfigFormat: "continue-dir"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "Codex",
|
|
62
|
+
detected: existsSync(join(home, ".codex")),
|
|
63
|
+
mcpConfigPath: getCodexConfigPath(),
|
|
64
|
+
mcpConfigFormat: "codex-toml"
|
|
65
|
+
}
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/utils/ide-registrar.ts
|
|
70
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync, appendFileSync } from "fs";
|
|
71
|
+
import { join as join2, dirname } from "path";
|
|
72
|
+
import { homedir as homedir2 } from "os";
|
|
73
|
+
import { execSync } from "child_process";
|
|
74
|
+
function findNodeBinary() {
|
|
75
|
+
try {
|
|
76
|
+
return execSync("which node", { encoding: "utf-8" }).trim();
|
|
77
|
+
} catch {
|
|
78
|
+
return "node";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function findAgentcacheScript() {
|
|
82
|
+
try {
|
|
83
|
+
const binPath = execSync("which agentcache", { encoding: "utf-8" }).trim();
|
|
84
|
+
return binPath;
|
|
85
|
+
} catch {
|
|
86
|
+
return join2(dirname(dirname(__dirname)), "dist", "cli.js");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function isVscodeExtensionIde(ide) {
|
|
90
|
+
return ide.name === "Roo Code" || ide.name === "Continue";
|
|
91
|
+
}
|
|
92
|
+
var ALL_TOOLS = [
|
|
93
|
+
"loop_inject_context",
|
|
94
|
+
"loop_compile_submit",
|
|
95
|
+
"loop_compile_cluster",
|
|
96
|
+
"loop_compile_extract",
|
|
97
|
+
"loop_enforce",
|
|
98
|
+
"loop_save_observation",
|
|
99
|
+
"loop_get_knowledge",
|
|
100
|
+
"loop_deprecate_knowledge"
|
|
101
|
+
];
|
|
102
|
+
function registerMcpServer(ide) {
|
|
103
|
+
if (!ide.detected) return false;
|
|
104
|
+
if (ide.mcpConfigFormat === "claude-settings") {
|
|
105
|
+
return registerClaudeCode();
|
|
106
|
+
}
|
|
107
|
+
if (ide.mcpConfigFormat === "mcp-json") {
|
|
108
|
+
return registerMcpJson(ide);
|
|
109
|
+
}
|
|
110
|
+
if (ide.mcpConfigFormat === "continue-dir") {
|
|
111
|
+
return registerContinue(ide);
|
|
112
|
+
}
|
|
113
|
+
if (ide.mcpConfigFormat === "codex-toml") {
|
|
114
|
+
return registerCodex(ide);
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
function registerClaudeCode() {
|
|
119
|
+
const claudeJsonPath = join2(homedir2(), ".claude.json");
|
|
120
|
+
let config = {};
|
|
121
|
+
if (existsSync2(claudeJsonPath)) {
|
|
122
|
+
try {
|
|
123
|
+
config = JSON.parse(readFileSync(claudeJsonPath, "utf-8"));
|
|
124
|
+
} catch {
|
|
125
|
+
config = {};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
129
|
+
if (config.mcpServers.agentcache) return false;
|
|
130
|
+
config.mcpServers.agentcache = {
|
|
131
|
+
type: "stdio",
|
|
132
|
+
command: "agentcache",
|
|
133
|
+
args: ["serve"],
|
|
134
|
+
env: {}
|
|
135
|
+
};
|
|
136
|
+
writeFileSync(claudeJsonPath, JSON.stringify(config, null, 2));
|
|
137
|
+
const settingsPath = join2(homedir2(), ".claude", "settings.json");
|
|
138
|
+
if (existsSync2(join2(homedir2(), ".claude"))) {
|
|
139
|
+
let settings = {};
|
|
140
|
+
if (existsSync2(settingsPath)) {
|
|
141
|
+
try {
|
|
142
|
+
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
143
|
+
} catch {
|
|
144
|
+
settings = {};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!settings.permissions) settings.permissions = {};
|
|
148
|
+
if (!settings.permissions.allow) settings.permissions.allow = [];
|
|
149
|
+
const allowList = settings.permissions.allow;
|
|
150
|
+
const mcpPerms = ALL_TOOLS.map((t) => `mcp__agentcache__${t}`);
|
|
151
|
+
for (const perm of mcpPerms) {
|
|
152
|
+
if (!allowList.includes(perm)) {
|
|
153
|
+
allowList.push(perm);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
157
|
+
}
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
function registerMcpJson(ide) {
|
|
161
|
+
let config = {};
|
|
162
|
+
if (existsSync2(ide.mcpConfigPath)) {
|
|
163
|
+
try {
|
|
164
|
+
config = JSON.parse(readFileSync(ide.mcpConfigPath, "utf-8"));
|
|
165
|
+
} catch {
|
|
166
|
+
config = {};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
170
|
+
if (config.mcpServers.agentcache) return false;
|
|
171
|
+
if (isVscodeExtensionIde(ide)) {
|
|
172
|
+
const nodeBin = findNodeBinary();
|
|
173
|
+
const script = findAgentcacheScript();
|
|
174
|
+
config.mcpServers.agentcache = {
|
|
175
|
+
command: nodeBin,
|
|
176
|
+
args: [script, "serve"],
|
|
177
|
+
alwaysAllow: ALL_TOOLS,
|
|
178
|
+
disabled: false
|
|
179
|
+
};
|
|
180
|
+
} else {
|
|
181
|
+
const agentcacheBin = findAgentcacheScript();
|
|
182
|
+
config.mcpServers.agentcache = {
|
|
183
|
+
command: agentcacheBin,
|
|
184
|
+
args: ["serve"]
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
mkdirSync(dirname(ide.mcpConfigPath), { recursive: true });
|
|
188
|
+
writeFileSync(ide.mcpConfigPath, JSON.stringify(config, null, 2));
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
function registerContinue(ide) {
|
|
192
|
+
const configPath = ide.mcpConfigPath;
|
|
193
|
+
if (existsSync2(configPath)) {
|
|
194
|
+
try {
|
|
195
|
+
const existing = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
196
|
+
if (existing.mcpServers?.agentcache) return false;
|
|
197
|
+
} catch {
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const nodeBin = findNodeBinary();
|
|
201
|
+
const script = findAgentcacheScript();
|
|
202
|
+
const config = {
|
|
203
|
+
mcpServers: {
|
|
204
|
+
agentcache: {
|
|
205
|
+
command: nodeBin,
|
|
206
|
+
args: [script, "serve"]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
211
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
function registerCodex(ide) {
|
|
215
|
+
const configPath = ide.mcpConfigPath;
|
|
216
|
+
if (existsSync2(configPath)) {
|
|
217
|
+
const content = readFileSync(configPath, "utf-8");
|
|
218
|
+
if (content.includes("[mcp_servers.agentcache]")) return false;
|
|
219
|
+
}
|
|
220
|
+
const agentcacheBin = findAgentcacheScript();
|
|
221
|
+
const tomlBlock = `
|
|
222
|
+
[mcp_servers.agentcache]
|
|
223
|
+
command = "${agentcacheBin}"
|
|
224
|
+
args = ["serve"]
|
|
225
|
+
default_tools_approval_mode = "auto"
|
|
226
|
+
`;
|
|
227
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
228
|
+
if (existsSync2(configPath)) {
|
|
229
|
+
appendFileSync(configPath, tomlBlock);
|
|
230
|
+
} else {
|
|
231
|
+
writeFileSync(configPath, tomlBlock.trimStart());
|
|
232
|
+
}
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
function registerClaudeHooks() {
|
|
236
|
+
const settingsPath = join2(homedir2(), ".claude", "settings.json");
|
|
237
|
+
if (!existsSync2(join2(homedir2(), ".claude"))) return false;
|
|
238
|
+
let settings = {};
|
|
239
|
+
if (existsSync2(settingsPath)) {
|
|
240
|
+
try {
|
|
241
|
+
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
242
|
+
} catch {
|
|
243
|
+
settings = {};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (!settings.hooks) settings.hooks = {};
|
|
247
|
+
const hooks = settings.hooks;
|
|
248
|
+
const loopHooks = {
|
|
249
|
+
Stop: [{ matcher: "", hooks: [{ type: "command", command: "agentcache compile-session" }] }],
|
|
250
|
+
SessionStart: [{ matcher: "", hooks: [{ type: "command", command: "agentcache discover" }] }],
|
|
251
|
+
PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: "agentcache enforce" }] }]
|
|
252
|
+
};
|
|
253
|
+
let registered = false;
|
|
254
|
+
for (const [event, hookConfig] of Object.entries(loopHooks)) {
|
|
255
|
+
if (!hooks[event]) hooks[event] = [];
|
|
256
|
+
const existing = hooks[event];
|
|
257
|
+
const hasLoop = existing.some((h) => h.hooks?.some((hh) => hh.command?.includes("agentcache")));
|
|
258
|
+
if (!hasLoop) {
|
|
259
|
+
hooks[event].push(...hookConfig);
|
|
260
|
+
registered = true;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (registered) {
|
|
264
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
265
|
+
}
|
|
266
|
+
return registered;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export {
|
|
270
|
+
detectInstalledIdes,
|
|
271
|
+
registerMcpServer,
|
|
272
|
+
registerClaudeHooks
|
|
273
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Command } from "commander";
|
|
|
5
5
|
var program = new Command();
|
|
6
6
|
program.name("agentcache").description("Engineering Knowledge Compiler \u2014 universal, zero-config").version("0.3.0");
|
|
7
7
|
program.command("setup").description("Detect IDEs and register Loop (runs automatically on install)").action(async () => {
|
|
8
|
-
const { runSetup } = await import("./setup-
|
|
8
|
+
const { runSetup } = await import("./setup-YHD2FO4N.js");
|
|
9
9
|
await runSetup();
|
|
10
10
|
});
|
|
11
11
|
program.command("serve").description("Start Loop MCP server (spawned by IDEs automatically)").action(async () => {
|
package/dist/postinstall.js
CHANGED
package/package.json
CHANGED
package/dist/chunk-7FIT3LNA.js
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
// src/utils/ide-detector.ts
|
|
2
|
-
import { existsSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
import { homedir } from "os";
|
|
5
|
-
function getRooConfigPath() {
|
|
6
|
-
const home = homedir();
|
|
7
|
-
if (process.platform === "darwin") {
|
|
8
|
-
return join(home, "Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json");
|
|
9
|
-
}
|
|
10
|
-
if (process.platform === "win32") {
|
|
11
|
-
return join(process.env.APPDATA || join(home, "AppData/Roaming"), "Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json");
|
|
12
|
-
}
|
|
13
|
-
return join(home, ".config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json");
|
|
14
|
-
}
|
|
15
|
-
function rooDetected() {
|
|
16
|
-
return existsSync(getRooConfigPath());
|
|
17
|
-
}
|
|
18
|
-
function detectInstalledIdes() {
|
|
19
|
-
const home = homedir();
|
|
20
|
-
return [
|
|
21
|
-
{
|
|
22
|
-
name: "Claude Code",
|
|
23
|
-
detected: existsSync(join(home, ".claude")),
|
|
24
|
-
mcpConfigPath: join(home, ".claude.json"),
|
|
25
|
-
mcpConfigFormat: "claude-settings"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
name: "Cursor",
|
|
29
|
-
detected: existsSync(join(home, ".cursor")),
|
|
30
|
-
mcpConfigPath: join(home, ".cursor", "mcp.json"),
|
|
31
|
-
mcpConfigFormat: "mcp-json"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: "Roo Code",
|
|
35
|
-
detected: rooDetected(),
|
|
36
|
-
mcpConfigPath: getRooConfigPath(),
|
|
37
|
-
mcpConfigFormat: "mcp-json"
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
name: "Windsurf",
|
|
41
|
-
detected: existsSync(join(home, ".windsurf")),
|
|
42
|
-
mcpConfigPath: join(home, ".windsurf", "mcp.json"),
|
|
43
|
-
mcpConfigFormat: "mcp-json"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: "Continue",
|
|
47
|
-
detected: existsSync(join(home, ".continue")),
|
|
48
|
-
mcpConfigPath: join(home, ".continue", "mcp.json"),
|
|
49
|
-
mcpConfigFormat: "mcp-json"
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: "Codex",
|
|
53
|
-
detected: existsSync(join(home, ".codex")),
|
|
54
|
-
mcpConfigPath: join(home, ".codex", "mcp.json"),
|
|
55
|
-
mcpConfigFormat: "mcp-json"
|
|
56
|
-
}
|
|
57
|
-
];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// src/utils/ide-registrar.ts
|
|
61
|
-
import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
62
|
-
import { join as join2, dirname } from "path";
|
|
63
|
-
import { homedir as homedir2 } from "os";
|
|
64
|
-
import { execSync } from "child_process";
|
|
65
|
-
function findNodeBinary() {
|
|
66
|
-
try {
|
|
67
|
-
return execSync("which node", { encoding: "utf-8" }).trim();
|
|
68
|
-
} catch {
|
|
69
|
-
return "node";
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function findAgentcacheScript() {
|
|
73
|
-
try {
|
|
74
|
-
const binPath = execSync("which agentcache", { encoding: "utf-8" }).trim();
|
|
75
|
-
return binPath;
|
|
76
|
-
} catch {
|
|
77
|
-
return join2(dirname(dirname(__dirname)), "dist", "cli.js");
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
function isVscodeExtensionIde(ide) {
|
|
81
|
-
return ide.name === "Roo Code";
|
|
82
|
-
}
|
|
83
|
-
var ALL_TOOLS = [
|
|
84
|
-
"loop_inject_context",
|
|
85
|
-
"loop_compile_submit",
|
|
86
|
-
"loop_compile_cluster",
|
|
87
|
-
"loop_compile_extract",
|
|
88
|
-
"loop_enforce",
|
|
89
|
-
"loop_save_observation",
|
|
90
|
-
"loop_get_knowledge",
|
|
91
|
-
"loop_deprecate_knowledge"
|
|
92
|
-
];
|
|
93
|
-
function registerMcpServer(ide) {
|
|
94
|
-
if (!ide.detected) return false;
|
|
95
|
-
if (ide.mcpConfigFormat === "claude-settings") {
|
|
96
|
-
const claudeJsonPath = join2(homedir2(), ".claude.json");
|
|
97
|
-
let config = {};
|
|
98
|
-
if (existsSync2(claudeJsonPath)) {
|
|
99
|
-
try {
|
|
100
|
-
config = JSON.parse(readFileSync(claudeJsonPath, "utf-8"));
|
|
101
|
-
} catch {
|
|
102
|
-
config = {};
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (!config.mcpServers) config.mcpServers = {};
|
|
106
|
-
if (config.mcpServers.agentcache) return false;
|
|
107
|
-
config.mcpServers.agentcache = {
|
|
108
|
-
type: "stdio",
|
|
109
|
-
command: "agentcache",
|
|
110
|
-
args: ["serve"],
|
|
111
|
-
env: {}
|
|
112
|
-
};
|
|
113
|
-
writeFileSync(claudeJsonPath, JSON.stringify(config, null, 2));
|
|
114
|
-
const settingsPath = join2(homedir2(), ".claude", "settings.json");
|
|
115
|
-
if (existsSync2(join2(homedir2(), ".claude"))) {
|
|
116
|
-
let settings = {};
|
|
117
|
-
if (existsSync2(settingsPath)) {
|
|
118
|
-
try {
|
|
119
|
-
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
120
|
-
} catch {
|
|
121
|
-
settings = {};
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
if (!settings.permissions) settings.permissions = {};
|
|
125
|
-
if (!settings.permissions.allow) settings.permissions.allow = [];
|
|
126
|
-
const allowList = settings.permissions.allow;
|
|
127
|
-
const mcpPerms = ALL_TOOLS.map((t) => `mcp__agentcache__${t}`);
|
|
128
|
-
for (const perm of mcpPerms) {
|
|
129
|
-
if (!allowList.includes(perm)) {
|
|
130
|
-
allowList.push(perm);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
134
|
-
}
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
if (ide.mcpConfigFormat === "mcp-json") {
|
|
138
|
-
let config = {};
|
|
139
|
-
if (existsSync2(ide.mcpConfigPath)) {
|
|
140
|
-
try {
|
|
141
|
-
config = JSON.parse(readFileSync(ide.mcpConfigPath, "utf-8"));
|
|
142
|
-
} catch {
|
|
143
|
-
config = {};
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if (!config.mcpServers) config.mcpServers = {};
|
|
147
|
-
if (config.mcpServers.agentcache) return false;
|
|
148
|
-
if (isVscodeExtensionIde(ide)) {
|
|
149
|
-
const nodeBin = findNodeBinary();
|
|
150
|
-
const script = findAgentcacheScript();
|
|
151
|
-
config.mcpServers.agentcache = {
|
|
152
|
-
command: nodeBin,
|
|
153
|
-
args: [script, "serve"],
|
|
154
|
-
alwaysAllow: ALL_TOOLS,
|
|
155
|
-
disabled: false
|
|
156
|
-
};
|
|
157
|
-
} else {
|
|
158
|
-
config.mcpServers.agentcache = {
|
|
159
|
-
command: "agentcache",
|
|
160
|
-
args: ["serve"]
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
mkdirSync(dirname(ide.mcpConfigPath), { recursive: true });
|
|
164
|
-
writeFileSync(ide.mcpConfigPath, JSON.stringify(config, null, 2));
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
function registerClaudeHooks() {
|
|
170
|
-
const settingsPath = join2(homedir2(), ".claude", "settings.json");
|
|
171
|
-
if (!existsSync2(join2(homedir2(), ".claude"))) return false;
|
|
172
|
-
let settings = {};
|
|
173
|
-
if (existsSync2(settingsPath)) {
|
|
174
|
-
try {
|
|
175
|
-
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
176
|
-
} catch {
|
|
177
|
-
settings = {};
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
if (!settings.hooks) settings.hooks = {};
|
|
181
|
-
const hooks = settings.hooks;
|
|
182
|
-
const loopHooks = {
|
|
183
|
-
Stop: [{ matcher: "", hooks: [{ type: "command", command: "agentcache compile-session" }] }],
|
|
184
|
-
SessionStart: [{ matcher: "", hooks: [{ type: "command", command: "agentcache discover" }] }],
|
|
185
|
-
PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: "agentcache enforce" }] }]
|
|
186
|
-
};
|
|
187
|
-
let registered = false;
|
|
188
|
-
for (const [event, hookConfig] of Object.entries(loopHooks)) {
|
|
189
|
-
if (!hooks[event]) hooks[event] = [];
|
|
190
|
-
const existing = hooks[event];
|
|
191
|
-
const hasLoop = existing.some((h) => h.hooks?.some((hh) => hh.command?.includes("agentcache")));
|
|
192
|
-
if (!hasLoop) {
|
|
193
|
-
hooks[event].push(...hookConfig);
|
|
194
|
-
registered = true;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
if (registered) {
|
|
198
|
-
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
199
|
-
}
|
|
200
|
-
return registered;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
export {
|
|
204
|
-
detectInstalledIdes,
|
|
205
|
-
registerMcpServer,
|
|
206
|
-
registerClaudeHooks
|
|
207
|
-
};
|