agentcache 0.1.2 → 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 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-5Z5AHD2U.js");
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 () => {
@@ -2,7 +2,7 @@ import {
2
2
  detectInstalledIdes,
3
3
  registerClaudeHooks,
4
4
  registerMcpServer
5
- } from "./chunk-LYEV4B2E.js";
5
+ } from "./chunk-MKQKD2PY.js";
6
6
  import {
7
7
  getDbPath,
8
8
  getGlobalLoopDir
@@ -2,7 +2,7 @@ import {
2
2
  detectInstalledIdes,
3
3
  registerClaudeHooks,
4
4
  registerMcpServer
5
- } from "./chunk-LYEV4B2E.js";
5
+ } from "./chunk-MKQKD2PY.js";
6
6
  import {
7
7
  getDbPath,
8
8
  getGlobalLoopDir
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "agentcache",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Knowledge cache for AI agents — learns how you work, remembers across sessions, works everywhere",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "a21.ai",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/raghav-a21ai/loop-eng"
10
+ "url": "https://github.com/raghav-a21ai/agentcache"
11
11
  },
12
- "homepage": "https://github.com/raghav-a21ai/loop-eng#readme",
12
+ "homepage": "https://github.com/raghav-a21ai/agentcache#readme",
13
13
  "keywords": [
14
14
  "ai",
15
15
  "mcp",
@@ -1,135 +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 detectInstalledIdes() {
6
- const home = homedir();
7
- return [
8
- {
9
- name: "Claude Code",
10
- detected: existsSync(join(home, ".claude")),
11
- mcpConfigPath: join(home, ".claude", "settings.json"),
12
- mcpConfigFormat: "claude-settings"
13
- },
14
- {
15
- name: "Cursor",
16
- detected: existsSync(join(home, ".cursor")),
17
- mcpConfigPath: join(home, ".cursor", "mcp.json"),
18
- mcpConfigFormat: "mcp-json"
19
- },
20
- {
21
- name: "Roo Code",
22
- detected: existsSync(join(home, ".roo")),
23
- mcpConfigPath: join(home, ".roo", "mcp.json"),
24
- mcpConfigFormat: "mcp-json"
25
- },
26
- {
27
- name: "Windsurf",
28
- detected: existsSync(join(home, ".windsurf")),
29
- mcpConfigPath: join(home, ".windsurf", "mcp.json"),
30
- mcpConfigFormat: "mcp-json"
31
- },
32
- {
33
- name: "Continue",
34
- detected: existsSync(join(home, ".continue")),
35
- mcpConfigPath: join(home, ".continue", "mcp.json"),
36
- mcpConfigFormat: "mcp-json"
37
- },
38
- {
39
- name: "Codex",
40
- detected: existsSync(join(home, ".codex")),
41
- mcpConfigPath: join(home, ".codex", "mcp.json"),
42
- mcpConfigFormat: "mcp-json"
43
- }
44
- ];
45
- }
46
-
47
- // src/utils/ide-registrar.ts
48
- import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
49
- import { join as join2, dirname } from "path";
50
- import { homedir as homedir2 } from "os";
51
- var MCP_ENTRY_STDIO = {
52
- type: "stdio",
53
- command: "agentcache",
54
- args: ["serve"],
55
- env: {}
56
- };
57
- var MCP_ENTRY_SIMPLE = {
58
- command: "agentcache",
59
- args: ["serve"]
60
- };
61
- function registerMcpServer(ide) {
62
- if (!ide.detected) return false;
63
- if (ide.mcpConfigFormat === "claude-settings") {
64
- const claudeJsonPath = join2(homedir2(), ".claude.json");
65
- let config = {};
66
- if (existsSync2(claudeJsonPath)) {
67
- try {
68
- config = JSON.parse(readFileSync(claudeJsonPath, "utf-8"));
69
- } catch {
70
- config = {};
71
- }
72
- }
73
- if (!config.mcpServers) config.mcpServers = {};
74
- if (config.mcpServers.agentcache) return false;
75
- config.mcpServers.agentcache = MCP_ENTRY_STDIO;
76
- writeFileSync(claudeJsonPath, JSON.stringify(config, null, 2));
77
- return true;
78
- }
79
- if (ide.mcpConfigFormat === "mcp-json") {
80
- let config = {};
81
- if (existsSync2(ide.mcpConfigPath)) {
82
- try {
83
- config = JSON.parse(readFileSync(ide.mcpConfigPath, "utf-8"));
84
- } catch {
85
- config = {};
86
- }
87
- }
88
- if (!config.mcpServers) config.mcpServers = {};
89
- if (config.mcpServers.agentcache) return false;
90
- config.mcpServers.agentcache = MCP_ENTRY_SIMPLE;
91
- mkdirSync(dirname(ide.mcpConfigPath), { recursive: true });
92
- writeFileSync(ide.mcpConfigPath, JSON.stringify(config, null, 2));
93
- return true;
94
- }
95
- return false;
96
- }
97
- function registerClaudeHooks() {
98
- const settingsPath = join2(homedir2(), ".claude", "settings.json");
99
- if (!existsSync2(join2(homedir2(), ".claude"))) return false;
100
- let settings = {};
101
- if (existsSync2(settingsPath)) {
102
- try {
103
- settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
104
- } catch {
105
- settings = {};
106
- }
107
- }
108
- if (!settings.hooks) settings.hooks = {};
109
- const hooks = settings.hooks;
110
- const loopHooks = {
111
- Stop: [{ matcher: "", hooks: [{ type: "command", command: "agentcache compile-session" }] }],
112
- SessionStart: [{ matcher: "", hooks: [{ type: "command", command: "agentcache discover" }] }],
113
- PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: "agentcache enforce" }] }]
114
- };
115
- let registered = false;
116
- for (const [event, hookConfig] of Object.entries(loopHooks)) {
117
- if (!hooks[event]) hooks[event] = [];
118
- const existing = hooks[event];
119
- const hasLoop = existing.some((h) => h.hooks?.some((hh) => hh.command?.includes("agentcache")));
120
- if (!hasLoop) {
121
- hooks[event].push(...hookConfig);
122
- registered = true;
123
- }
124
- }
125
- if (registered) {
126
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
127
- }
128
- return registered;
129
- }
130
-
131
- export {
132
- detectInstalledIdes,
133
- registerMcpServer,
134
- registerClaudeHooks
135
- };