agentcache 0.1.3 → 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/README.md +35 -26
- package/dist/chunk-LDQPTAZ7.js +282 -0
- package/dist/{chunk-QGG25FWV.js → chunk-OSFK44XC.js} +1 -1
- package/dist/{chunk-PYGRUQNL.js → chunk-T7BJPANN.js} +3 -3
- package/dist/{chunk-VPEEZXLK.js → chunk-WHP4Z32Z.js} +17 -8
- package/dist/cli.js +11 -11
- package/dist/mcp.js +32 -32
- package/dist/{paths-TWJ7GAJY.js → paths-LEZQCRKI.js} +7 -5
- package/dist/postinstall.js +6 -4
- package/dist/{pre-tool-use-2P5P6JWE.js → pre-tool-use-TPCPTJXS.js} +4 -4
- package/dist/{session-start-ILEPFZZC.js → session-start-BIY7CBXU.js} +4 -4
- package/dist/{setup-C6PI6YD7.js → setup-YCFTG2KT.js} +8 -6
- package/dist/{stop-DRL3LXFQ.js → stop-6MKD743B.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-7FIT3LNA.js +0 -207
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ npm install -g agentcache
|
|
|
23
23
|
Done. Start a new session in any IDE. AgentCache is already running.
|
|
24
24
|
|
|
25
25
|
No `init`. No `setup`. No config. No second command. The install itself:
|
|
26
|
-
1. Creates `~/.
|
|
26
|
+
1. Creates `~/.agentcache/agentcache.db` (your knowledge store)
|
|
27
27
|
2. Detects installed IDEs (Claude Code, Cursor, Roo Code, Windsurf, Continue, Codex)
|
|
28
28
|
3. Registers itself as an MCP server in each
|
|
29
29
|
4. Sets up Claude Code hooks for automatic transcript recovery
|
|
@@ -49,7 +49,8 @@ No `init`. No `setup`. No config. No second command. The install itself:
|
|
|
49
49
|
│ └────────────┬────────────┘ │
|
|
50
50
|
│ │ │
|
|
51
51
|
│ ┌─────────┴─────────┐ │
|
|
52
|
-
│ │
|
|
52
|
+
│ │ ~/.agentcache/ │ │
|
|
53
|
+
│ │ agentcache.db │ │
|
|
53
54
|
│ │ (SQLite + WAL) │ │
|
|
54
55
|
│ └───────────────────┘ │
|
|
55
56
|
└─────────────────────────────────────────────────────────────────┘
|
|
@@ -57,8 +58,8 @@ No `init`. No `setup`. No config. No second command. The install itself:
|
|
|
57
58
|
|
|
58
59
|
### The Cycle
|
|
59
60
|
|
|
60
|
-
1. **Session starts** — agent calls `
|
|
61
|
-
2. **During session** — agent calls `
|
|
61
|
+
1. **Session starts** — agent calls `agentcache_inject_context` → gets compiled rules, lessons, decisions
|
|
62
|
+
2. **During session** — agent calls `agentcache_compile_submit` incrementally as it learns things
|
|
62
63
|
3. **Session ends** — knowledge is already saved. If agent didn't submit (abrupt exit), transcript recovery handles it next session.
|
|
63
64
|
|
|
64
65
|
### Knowledge Types
|
|
@@ -78,14 +79,14 @@ AgentCache exposes 8 tools via the Model Context Protocol:
|
|
|
78
79
|
|
|
79
80
|
| Tool | Purpose |
|
|
80
81
|
|------|---------|
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
82
|
+
| `agentcache_inject_context` | Load compiled knowledge at session start |
|
|
83
|
+
| `agentcache_compile_submit` | Submit observations incrementally during session |
|
|
84
|
+
| `agentcache_compile_cluster` | Resolve clustering when observations overlap existing knowledge |
|
|
85
|
+
| `agentcache_compile_extract` | Process queued transcripts from previous sessions |
|
|
86
|
+
| `agentcache_enforce` | Check tool calls against enforced policy rules |
|
|
87
|
+
| `agentcache_save_observation` | Save a permanent observation (USER authority, never auto-deprecated) |
|
|
88
|
+
| `agentcache_get_knowledge` | Query the knowledge database |
|
|
89
|
+
| `agentcache_deprecate_knowledge` | Mark knowledge as deprecated when it's no longer valid |
|
|
89
90
|
|
|
90
91
|
## CLI Commands
|
|
91
92
|
|
|
@@ -114,7 +115,7 @@ AgentCache uses MCP (Model Context Protocol) as its only interface. Any IDE that
|
|
|
114
115
|
|
|
115
116
|
### Developer-Scoped
|
|
116
117
|
|
|
117
|
-
One database per developer (`~/.
|
|
118
|
+
One database per developer (`~/.agentcache/agentcache.db`), not per project. Rules and lessons learned in one project benefit all your projects. Project-specific decisions stay scoped to their project.
|
|
118
119
|
|
|
119
120
|
### Resilient to Abrupt Exits
|
|
120
121
|
|
|
@@ -133,24 +134,32 @@ AgentCache prevents knowledge from growing unbounded:
|
|
|
133
134
|
|
|
134
135
|
## Supported IDEs
|
|
135
136
|
|
|
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 | — |
|
|
137
|
+
| IDE | MCP | Auto-Approve | Transcript Recovery | Hooks |
|
|
138
|
+
|-----|-----|-------------|--------------------|----|
|
|
139
|
+
| Claude Code | Yes | Yes (automatic) | Full (JSONL) | Stop, SessionStart, PreToolUse |
|
|
140
|
+
| Cursor | Yes | **Manual** (see below) | Incremental only | — |
|
|
141
|
+
| Roo Code | Yes | Yes (automatic) | Incremental only | — |
|
|
142
|
+
| Windsurf | Yes | Yes (automatic) | Incremental only | — |
|
|
143
|
+
| Continue | Yes | Yes (automatic) | Full (JSON) | — |
|
|
144
|
+
| Codex | Yes | Yes (automatic) | Incremental only | — |
|
|
144
145
|
|
|
145
146
|
"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
147
|
|
|
148
|
+
### Cursor: Enable Auto-Approve
|
|
149
|
+
|
|
150
|
+
Cursor does not support programmatic auto-approve for MCP tools. After installing, you need to manually enable it once:
|
|
151
|
+
|
|
152
|
+
1. Open Cursor Settings → MCP
|
|
153
|
+
2. Find **agentcache** in the server list
|
|
154
|
+
3. Set tool approval to **"Always allow"** (or enable "Yolo mode" in Cursor settings for all tools)
|
|
155
|
+
|
|
147
156
|
## Data Storage
|
|
148
157
|
|
|
149
|
-
All data lives in `~/.
|
|
158
|
+
All data lives in `~/.agentcache/agentcache.db` (SQLite with WAL mode for concurrent access).
|
|
150
159
|
|
|
151
160
|
```
|
|
152
|
-
~/.
|
|
153
|
-
└──
|
|
161
|
+
~/.agentcache/
|
|
162
|
+
└── agentcache.db # All knowledge, observations, sessions, pending queue
|
|
154
163
|
```
|
|
155
164
|
|
|
156
165
|
No data leaves your machine. No network calls. No telemetry. No accounts.
|
|
@@ -185,8 +194,8 @@ Projects are identified by a hash of their full filesystem path, not just the fo
|
|
|
185
194
|
## Contributing
|
|
186
195
|
|
|
187
196
|
```bash
|
|
188
|
-
git clone https://github.com/raghav-a21ai/
|
|
189
|
-
cd
|
|
197
|
+
git clone https://github.com/raghav-a21ai/agentcache
|
|
198
|
+
cd agentcache
|
|
190
199
|
npm install
|
|
191
200
|
npm run build
|
|
192
201
|
npm test
|
|
@@ -0,0 +1,282 @@
|
|
|
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
|
+
"agentcache_inject_context",
|
|
94
|
+
"agentcache_compile_submit",
|
|
95
|
+
"agentcache_compile_cluster",
|
|
96
|
+
"agentcache_compile_extract",
|
|
97
|
+
"agentcache_enforce",
|
|
98
|
+
"agentcache_save_observation",
|
|
99
|
+
"agentcache_get_knowledge",
|
|
100
|
+
"agentcache_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
|
+
let serverRegistered = false;
|
|
130
|
+
if (!config.mcpServers.agentcache) {
|
|
131
|
+
config.mcpServers.agentcache = {
|
|
132
|
+
type: "stdio",
|
|
133
|
+
command: "agentcache",
|
|
134
|
+
args: ["serve"],
|
|
135
|
+
env: {}
|
|
136
|
+
};
|
|
137
|
+
writeFileSync(claudeJsonPath, JSON.stringify(config, null, 2));
|
|
138
|
+
serverRegistered = true;
|
|
139
|
+
}
|
|
140
|
+
const settingsPath = join2(homedir2(), ".claude", "settings.json");
|
|
141
|
+
if (existsSync2(join2(homedir2(), ".claude"))) {
|
|
142
|
+
let settings = {};
|
|
143
|
+
if (existsSync2(settingsPath)) {
|
|
144
|
+
try {
|
|
145
|
+
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
146
|
+
} catch {
|
|
147
|
+
settings = {};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (!settings.permissions) settings.permissions = {};
|
|
151
|
+
if (!settings.permissions.allow) settings.permissions.allow = [];
|
|
152
|
+
const allowList = settings.permissions.allow;
|
|
153
|
+
const mcpPerms = ALL_TOOLS.map((t) => `mcp__agentcache__${t}`);
|
|
154
|
+
let permsUpdated = false;
|
|
155
|
+
for (const perm of mcpPerms) {
|
|
156
|
+
if (!allowList.includes(perm)) {
|
|
157
|
+
allowList.push(perm);
|
|
158
|
+
permsUpdated = true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (permsUpdated) {
|
|
162
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return serverRegistered;
|
|
166
|
+
}
|
|
167
|
+
function registerMcpJson(ide) {
|
|
168
|
+
let config = {};
|
|
169
|
+
if (existsSync2(ide.mcpConfigPath)) {
|
|
170
|
+
try {
|
|
171
|
+
config = JSON.parse(readFileSync(ide.mcpConfigPath, "utf-8"));
|
|
172
|
+
} catch {
|
|
173
|
+
config = {};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
177
|
+
const existing = config.mcpServers.agentcache;
|
|
178
|
+
if (isVscodeExtensionIde(ide)) {
|
|
179
|
+
const nodeBin = findNodeBinary();
|
|
180
|
+
const script = findAgentcacheScript();
|
|
181
|
+
config.mcpServers.agentcache = {
|
|
182
|
+
command: nodeBin,
|
|
183
|
+
args: [script, "serve"],
|
|
184
|
+
alwaysAllow: ALL_TOOLS,
|
|
185
|
+
disabled: false
|
|
186
|
+
};
|
|
187
|
+
} else if (!existing) {
|
|
188
|
+
const agentcacheBin = findAgentcacheScript();
|
|
189
|
+
config.mcpServers.agentcache = {
|
|
190
|
+
command: agentcacheBin,
|
|
191
|
+
args: ["serve"]
|
|
192
|
+
};
|
|
193
|
+
} else {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
mkdirSync(dirname(ide.mcpConfigPath), { recursive: true });
|
|
197
|
+
writeFileSync(ide.mcpConfigPath, JSON.stringify(config, null, 2));
|
|
198
|
+
return !existing;
|
|
199
|
+
}
|
|
200
|
+
function registerContinue(ide) {
|
|
201
|
+
const configPath = ide.mcpConfigPath;
|
|
202
|
+
if (existsSync2(configPath)) {
|
|
203
|
+
try {
|
|
204
|
+
const existing = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
205
|
+
if (existing.mcpServers?.agentcache) return false;
|
|
206
|
+
} catch {
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const nodeBin = findNodeBinary();
|
|
210
|
+
const script = findAgentcacheScript();
|
|
211
|
+
const config = {
|
|
212
|
+
mcpServers: {
|
|
213
|
+
agentcache: {
|
|
214
|
+
command: nodeBin,
|
|
215
|
+
args: [script, "serve"]
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
220
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
function registerCodex(ide) {
|
|
224
|
+
const configPath = ide.mcpConfigPath;
|
|
225
|
+
if (existsSync2(configPath)) {
|
|
226
|
+
const content = readFileSync(configPath, "utf-8");
|
|
227
|
+
if (content.includes("[mcp_servers.agentcache]")) return false;
|
|
228
|
+
}
|
|
229
|
+
const agentcacheBin = findAgentcacheScript();
|
|
230
|
+
const tomlBlock = `
|
|
231
|
+
[mcp_servers.agentcache]
|
|
232
|
+
command = "${agentcacheBin}"
|
|
233
|
+
args = ["serve"]
|
|
234
|
+
default_tools_approval_mode = "auto"
|
|
235
|
+
`;
|
|
236
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
237
|
+
if (existsSync2(configPath)) {
|
|
238
|
+
appendFileSync(configPath, tomlBlock);
|
|
239
|
+
} else {
|
|
240
|
+
writeFileSync(configPath, tomlBlock.trimStart());
|
|
241
|
+
}
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
function registerClaudeHooks() {
|
|
245
|
+
const settingsPath = join2(homedir2(), ".claude", "settings.json");
|
|
246
|
+
if (!existsSync2(join2(homedir2(), ".claude"))) return false;
|
|
247
|
+
let settings = {};
|
|
248
|
+
if (existsSync2(settingsPath)) {
|
|
249
|
+
try {
|
|
250
|
+
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
251
|
+
} catch {
|
|
252
|
+
settings = {};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (!settings.hooks) settings.hooks = {};
|
|
256
|
+
const hooks = settings.hooks;
|
|
257
|
+
const agentcacheHooks = {
|
|
258
|
+
Stop: [{ matcher: "", hooks: [{ type: "command", command: "agentcache compile-session" }] }],
|
|
259
|
+
SessionStart: [{ matcher: "", hooks: [{ type: "command", command: "agentcache discover" }] }],
|
|
260
|
+
PreToolUse: [{ matcher: "", hooks: [{ type: "command", command: "agentcache enforce" }] }]
|
|
261
|
+
};
|
|
262
|
+
let registered = false;
|
|
263
|
+
for (const [event, hookConfig] of Object.entries(agentcacheHooks)) {
|
|
264
|
+
if (!hooks[event]) hooks[event] = [];
|
|
265
|
+
const existing = hooks[event];
|
|
266
|
+
const hasAgentcache = existing.some((h) => h.hooks?.some((hh) => hh.command?.includes("agentcache")));
|
|
267
|
+
if (!hasAgentcache) {
|
|
268
|
+
hooks[event].push(...hookConfig);
|
|
269
|
+
registered = true;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (registered) {
|
|
273
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
274
|
+
}
|
|
275
|
+
return registered;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export {
|
|
279
|
+
detectInstalledIdes,
|
|
280
|
+
registerMcpServer,
|
|
281
|
+
registerClaudeHooks
|
|
282
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/policy/engine.ts
|
|
2
2
|
var HARDCODED_BLOCKS = [
|
|
3
|
-
{ pattern: /git\s+push\s+--force\s+(origin\s+)?(main|master)/i, reason: "Force-push to main/master is blocked by
|
|
4
|
-
{ pattern: /rm\s+-rf\s+[\/~]/i, reason: "Destructive rm -rf on root or home is blocked by
|
|
5
|
-
{ pattern: />\s*(.*\.(env|pem|key))/i, reason: "Writing to sensitive files (.env, .pem, .key) is blocked by
|
|
3
|
+
{ pattern: /git\s+push\s+--force\s+(origin\s+)?(main|master)/i, reason: "Force-push to main/master is blocked by AgentCache policy" },
|
|
4
|
+
{ pattern: /rm\s+-rf\s+[\/~]/i, reason: "Destructive rm -rf on root or home is blocked by AgentCache policy" },
|
|
5
|
+
{ pattern: />\s*(.*\.(env|pem|key))/i, reason: "Writing to sensitive files (.env, .pem, .key) is blocked by AgentCache policy" }
|
|
6
6
|
];
|
|
7
7
|
function evaluatePolicy(input, enforcedRules) {
|
|
8
8
|
const command = extractCommand(input.tool_name, input.tool_input);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/utils/paths.ts
|
|
2
|
-
import { existsSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
2
|
+
import { existsSync, renameSync, mkdirSync } from "fs";
|
|
3
|
+
import { join, dirname } from "path";
|
|
4
4
|
import { homedir } from "os";
|
|
5
5
|
import { createHash } from "crypto";
|
|
6
6
|
|
|
@@ -26,15 +26,23 @@ function getGitRoot(cwd) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// src/utils/paths.ts
|
|
29
|
-
function
|
|
30
|
-
return join(homedir(), ".
|
|
29
|
+
function getDataDir() {
|
|
30
|
+
return join(homedir(), ".agentcache");
|
|
31
31
|
}
|
|
32
32
|
function getDbPath() {
|
|
33
|
-
return join(
|
|
33
|
+
return join(getDataDir(), "agentcache.db");
|
|
34
34
|
}
|
|
35
|
-
function
|
|
35
|
+
function isInitialized() {
|
|
36
36
|
return existsSync(getDbPath());
|
|
37
37
|
}
|
|
38
|
+
function migrateFromLegacy() {
|
|
39
|
+
const legacyDb = join(homedir(), ".loop", "loop.db");
|
|
40
|
+
const newDb = getDbPath();
|
|
41
|
+
if (existsSync(legacyDb) && !existsSync(newDb)) {
|
|
42
|
+
mkdirSync(dirname(newDb), { recursive: true });
|
|
43
|
+
renameSync(legacyDb, newDb);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
38
46
|
function findProjectRoot(cwd) {
|
|
39
47
|
const dir = cwd || process.cwd();
|
|
40
48
|
const gitRoot = getGitRoot(dir);
|
|
@@ -57,9 +65,10 @@ function getContinueSessionsDir() {
|
|
|
57
65
|
|
|
58
66
|
export {
|
|
59
67
|
getGitContext,
|
|
60
|
-
|
|
68
|
+
getDataDir,
|
|
61
69
|
getDbPath,
|
|
62
|
-
|
|
70
|
+
isInitialized,
|
|
71
|
+
migrateFromLegacy,
|
|
63
72
|
findProjectRoot,
|
|
64
73
|
getProjectId,
|
|
65
74
|
getProjectDisplayName,
|
package/dist/cli.js
CHANGED
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
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
|
-
program.command("setup").description("Detect IDEs and register
|
|
8
|
-
const { runSetup } = await import("./setup-
|
|
7
|
+
program.command("setup").description("Detect IDEs and register AgentCache (runs automatically on install)").action(async () => {
|
|
8
|
+
const { runSetup } = await import("./setup-YCFTG2KT.js");
|
|
9
9
|
await runSetup();
|
|
10
10
|
});
|
|
11
|
-
program.command("serve").description("Start
|
|
11
|
+
program.command("serve").description("Start AgentCache MCP server (spawned by IDEs automatically)").action(async () => {
|
|
12
12
|
const { startMcpServer } = await import("./mcp.js");
|
|
13
13
|
await startMcpServer();
|
|
14
14
|
});
|
|
15
15
|
program.command("compile-session").description("Stop hook: queue transcript for compilation").action(async () => {
|
|
16
|
-
const { handleStop } = await import("./stop-
|
|
16
|
+
const { handleStop } = await import("./stop-6MKD743B.js");
|
|
17
17
|
let payload;
|
|
18
18
|
try {
|
|
19
19
|
let data = "";
|
|
@@ -28,11 +28,11 @@ program.command("compile-session").description("Stop hook: queue transcript for
|
|
|
28
28
|
await handleStop(payload);
|
|
29
29
|
});
|
|
30
30
|
program.command("discover").description("SessionStart hook: discover uncompiled transcripts").action(async () => {
|
|
31
|
-
const { handleSessionStart } = await import("./session-start-
|
|
31
|
+
const { handleSessionStart } = await import("./session-start-BIY7CBXU.js");
|
|
32
32
|
await handleSessionStart();
|
|
33
33
|
});
|
|
34
34
|
program.command("enforce").description("PreToolUse hook: policy enforcement").action(async () => {
|
|
35
|
-
const { handlePreToolUse } = await import("./pre-tool-use-
|
|
35
|
+
const { handlePreToolUse } = await import("./pre-tool-use-TPCPTJXS.js");
|
|
36
36
|
let data = "";
|
|
37
37
|
for await (const chunk of process.stdin) {
|
|
38
38
|
data += chunk;
|
|
@@ -45,10 +45,10 @@ program.command("enforce").description("PreToolUse hook: policy enforcement").ac
|
|
|
45
45
|
process.stdout.write("{}");
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
|
-
program.command("status").description("Show
|
|
49
|
-
const { getDbPath,
|
|
50
|
-
if (!
|
|
51
|
-
console.log("
|
|
48
|
+
program.command("status").description("Show AgentCache knowledge stats").action(async () => {
|
|
49
|
+
const { getDbPath, isInitialized, findProjectRoot, getProjectId, getProjectDisplayName } = await import("./paths-LEZQCRKI.js");
|
|
50
|
+
if (!isInitialized()) {
|
|
51
|
+
console.log("AgentCache not initialized. Run: agentcache setup");
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
const { SqliteKnowledgeRepository } = await import("./sqlite-5V565IV3.js");
|
|
@@ -65,7 +65,7 @@ program.command("status").description("Show Loop knowledge stats").action(async
|
|
|
65
65
|
const projectItems = items.filter((i) => i.scope === "project");
|
|
66
66
|
const pending = repo.getPendingCount();
|
|
67
67
|
repo.close();
|
|
68
|
-
console.log(`
|
|
68
|
+
console.log(`AgentCache \u2014 ${displayName} (${project})`);
|
|
69
69
|
console.log(` ${items.length} items (${globalItems.length} global, ${projectItems.length} project)`);
|
|
70
70
|
console.log(` ${rules.length} rules | ${lessons.length} lessons | ${decisions.length} decisions | ${context.length} context`);
|
|
71
71
|
if (pending > 0) console.log(` ${pending} sessions pending compilation`);
|
package/dist/mcp.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
evaluatePolicy
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-T7BJPANN.js";
|
|
4
4
|
import {
|
|
5
5
|
parseTranscript
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OSFK44XC.js";
|
|
7
7
|
import {
|
|
8
8
|
findProjectRoot,
|
|
9
9
|
getDbPath,
|
|
10
10
|
getGitContext,
|
|
11
11
|
getProjectId,
|
|
12
|
-
|
|
13
|
-
} from "./chunk-
|
|
12
|
+
isInitialized
|
|
13
|
+
} from "./chunk-WHP4Z32Z.js";
|
|
14
14
|
import {
|
|
15
15
|
SqliteKnowledgeRepository
|
|
16
16
|
} from "./chunk-MMSMDJ4O.js";
|
|
@@ -528,7 +528,7 @@ function saveCompileRun(repo, sessionId, project, observationsProcessed, autoRei
|
|
|
528
528
|
}
|
|
529
529
|
function formatDiagnostics(extracted, autoReinforced, created, reinforced, superseded, deprecated, ignored, project, sessionId) {
|
|
530
530
|
return [
|
|
531
|
-
`
|
|
531
|
+
`AgentCache Compiler v${COMPILER_VERSION}`,
|
|
532
532
|
`Project: ${project} | Session: ${sessionId}`,
|
|
533
533
|
` ${extracted} observations processed`,
|
|
534
534
|
autoReinforced > 0 ? ` ${autoReinforced} auto-reinforced (no LLM needed)` : "",
|
|
@@ -570,7 +570,7 @@ async function startMcpServer() {
|
|
|
570
570
|
{ name: "agentcache", version: "0.1.0" },
|
|
571
571
|
{
|
|
572
572
|
capabilities: { tools: {} },
|
|
573
|
-
instructions: "AgentCache is your knowledge cache. At the START of every session, call
|
|
573
|
+
instructions: "AgentCache is your knowledge cache. At the START of every session, call agentcache_inject_context to load compiled rules, lessons, decisions, and context. Submit observations INCREMENTALLY via agentcache_compile_submit as you learn them \u2014 do not wait until session end."
|
|
574
574
|
}
|
|
575
575
|
);
|
|
576
576
|
server.oninitialized = async () => {
|
|
@@ -582,7 +582,7 @@ async function startMcpServer() {
|
|
|
582
582
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
583
583
|
tools: [
|
|
584
584
|
{
|
|
585
|
-
name: "
|
|
585
|
+
name: "agentcache_inject_context",
|
|
586
586
|
description: "Get compiled engineering knowledge for this project. Returns global rules/lessons (apply everywhere) + project-specific decisions/context. Call this at the START of every session.",
|
|
587
587
|
inputSchema: {
|
|
588
588
|
type: "object",
|
|
@@ -593,7 +593,7 @@ async function startMcpServer() {
|
|
|
593
593
|
}
|
|
594
594
|
},
|
|
595
595
|
{
|
|
596
|
-
name: "
|
|
596
|
+
name: "agentcache_compile_submit",
|
|
597
597
|
description: "Submit observations extracted from your session. Call this INCREMENTALLY \u2014 each time you learn a rule, lesson, decision, or context item. Do NOT batch until end of session; sessions can terminate without warning.",
|
|
598
598
|
inputSchema: {
|
|
599
599
|
type: "object",
|
|
@@ -619,12 +619,12 @@ async function startMcpServer() {
|
|
|
619
619
|
}
|
|
620
620
|
},
|
|
621
621
|
{
|
|
622
|
-
name: "
|
|
623
|
-
description: "Submit clustering decisions when
|
|
622
|
+
name: "agentcache_compile_cluster",
|
|
623
|
+
description: "Submit clustering decisions when agentcache_compile_submit returns needs_clustering. Determines whether observations create new knowledge or relate to existing items.",
|
|
624
624
|
inputSchema: {
|
|
625
625
|
type: "object",
|
|
626
626
|
properties: {
|
|
627
|
-
sessionId: { type: "string", description: "Session ID from
|
|
627
|
+
sessionId: { type: "string", description: "Session ID from agentcache_compile_submit response" },
|
|
628
628
|
clusters: {
|
|
629
629
|
type: "array",
|
|
630
630
|
items: {
|
|
@@ -644,8 +644,8 @@ async function startMcpServer() {
|
|
|
644
644
|
}
|
|
645
645
|
},
|
|
646
646
|
{
|
|
647
|
-
name: "
|
|
648
|
-
description: "For PREVIOUS sessions stored as transcript files. Reads a queued transcript and returns an extraction prompt for you to process. After processing, call
|
|
647
|
+
name: "agentcache_compile_extract",
|
|
648
|
+
description: "For PREVIOUS sessions stored as transcript files. Reads a queued transcript and returns an extraction prompt for you to process. After processing, call agentcache_compile_submit with the results.",
|
|
649
649
|
inputSchema: {
|
|
650
650
|
type: "object",
|
|
651
651
|
properties: {
|
|
@@ -655,8 +655,8 @@ async function startMcpServer() {
|
|
|
655
655
|
}
|
|
656
656
|
},
|
|
657
657
|
{
|
|
658
|
-
name: "
|
|
659
|
-
description: "Check if a tool call is allowed by
|
|
658
|
+
name: "agentcache_enforce",
|
|
659
|
+
description: "Check if a tool call is allowed by AgentCache policy rules. Call this BEFORE executing risky operations (file deletions, force pushes, etc). Returns allow or block with reason.",
|
|
660
660
|
inputSchema: {
|
|
661
661
|
type: "object",
|
|
662
662
|
properties: {
|
|
@@ -668,7 +668,7 @@ async function startMcpServer() {
|
|
|
668
668
|
}
|
|
669
669
|
},
|
|
670
670
|
{
|
|
671
|
-
name: "
|
|
671
|
+
name: "agentcache_save_observation",
|
|
672
672
|
description: "Save a single observation immediately with USER authority (never overwritten by compiler). Use for important rules or decisions that should persist permanently.",
|
|
673
673
|
inputSchema: {
|
|
674
674
|
type: "object",
|
|
@@ -683,8 +683,8 @@ async function startMcpServer() {
|
|
|
683
683
|
}
|
|
684
684
|
},
|
|
685
685
|
{
|
|
686
|
-
name: "
|
|
687
|
-
description: "Query knowledge items from
|
|
686
|
+
name: "agentcache_get_knowledge",
|
|
687
|
+
description: "Query knowledge items from AgentCache knowledge database.",
|
|
688
688
|
inputSchema: {
|
|
689
689
|
type: "object",
|
|
690
690
|
properties: {
|
|
@@ -697,7 +697,7 @@ async function startMcpServer() {
|
|
|
697
697
|
}
|
|
698
698
|
},
|
|
699
699
|
{
|
|
700
|
-
name: "
|
|
700
|
+
name: "agentcache_deprecate_knowledge",
|
|
701
701
|
description: "Mark a knowledge item as deprecated. Use when a rule, lesson, or decision is no longer valid. Works on both auto-compiled and user-saved items.",
|
|
702
702
|
inputSchema: {
|
|
703
703
|
type: "object",
|
|
@@ -711,15 +711,15 @@ async function startMcpServer() {
|
|
|
711
711
|
]
|
|
712
712
|
}));
|
|
713
713
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
714
|
-
if (!
|
|
715
|
-
return { content: [{ type: "text", text: "
|
|
714
|
+
if (!isInitialized()) {
|
|
715
|
+
return { content: [{ type: "text", text: "AgentCache not initialized. Run: agentcache setup" }], isError: true };
|
|
716
716
|
}
|
|
717
717
|
const repo = new SqliteKnowledgeRepository(getDbPath());
|
|
718
718
|
const projectRoot = getResolvedProjectRoot();
|
|
719
719
|
const detectedProject = getResolvedProjectId();
|
|
720
720
|
try {
|
|
721
721
|
switch (request.params.name) {
|
|
722
|
-
case "
|
|
722
|
+
case "agentcache_inject_context": {
|
|
723
723
|
const args = request.params.arguments || {};
|
|
724
724
|
const project = args.project || detectedProject;
|
|
725
725
|
const items = repo.getKnowledgeForContext(project);
|
|
@@ -740,17 +740,17 @@ async function startMcpServer() {
|
|
|
740
740
|
if (context.length) {
|
|
741
741
|
output += "# Context\n" + context.map((c) => `- ${c.content}`).join("\n") + "\n\n";
|
|
742
742
|
}
|
|
743
|
-
if (!output) output = "No compiled knowledge yet. This will populate as you use
|
|
743
|
+
if (!output) output = "No compiled knowledge yet. This will populate as you use AgentCache across sessions.\n";
|
|
744
744
|
const pendingCount = repo.getPendingCount();
|
|
745
745
|
if (pendingCount > 0) {
|
|
746
|
-
output = `<!-- ${pendingCount} previous session(s) pending compilation. Call
|
|
746
|
+
output = `<!-- ${pendingCount} previous session(s) pending compilation. Call agentcache_compile_extract to process. -->
|
|
747
747
|
|
|
748
748
|
` + output;
|
|
749
749
|
}
|
|
750
|
-
output += "\n---\nIMPORTANT: Submit observations incrementally as they happen during this session.\nWhen you learn something (rule, lesson, decision, context), call
|
|
750
|
+
output += "\n---\nIMPORTANT: Submit observations incrementally as they happen during this session.\nWhen you learn something (rule, lesson, decision, context), call agentcache_compile_submit immediately.\nDo NOT wait until the end \u2014 sessions can terminate without warning.\n";
|
|
751
751
|
return { content: [{ type: "text", text: output.trim() }] };
|
|
752
752
|
}
|
|
753
|
-
case "
|
|
753
|
+
case "agentcache_compile_submit": {
|
|
754
754
|
const args = request.params.arguments;
|
|
755
755
|
const project = args.project || detectedProject;
|
|
756
756
|
const sessionId = `sess_${randomUUID4().slice(0, 8)}`;
|
|
@@ -768,14 +768,14 @@ async function startMcpServer() {
|
|
|
768
768
|
content: [{ type: "text", text: JSON.stringify({ status: "needs_clustering", sessionId: result.sessionId, clusteringContext: result.clusteringPrompt }) }]
|
|
769
769
|
};
|
|
770
770
|
}
|
|
771
|
-
case "
|
|
771
|
+
case "agentcache_compile_cluster": {
|
|
772
772
|
const args = request.params.arguments;
|
|
773
773
|
const project = args.project || detectedProject;
|
|
774
774
|
const responseText = JSON.stringify({ clusters: args.clusters });
|
|
775
775
|
const result = processClustering(repo, responseText, args.sessionId, project, projectRoot);
|
|
776
776
|
return { content: [{ type: "text", text: JSON.stringify({ status: "complete", diagnostics: result.diagnostics }) }] };
|
|
777
777
|
}
|
|
778
|
-
case "
|
|
778
|
+
case "agentcache_compile_extract": {
|
|
779
779
|
const args = request.params.arguments || {};
|
|
780
780
|
const entry = repo.popPendingTranscript();
|
|
781
781
|
if (!entry) {
|
|
@@ -793,14 +793,14 @@ async function startMcpServer() {
|
|
|
793
793
|
const state = startCompile(events, sessionId, project, entry.projectRoot || projectRoot, repo, entry.transcriptPath);
|
|
794
794
|
return { content: [{ type: "text", text: JSON.stringify({ sessionId: state.sessionId, prompt: state.prompt }) }] };
|
|
795
795
|
}
|
|
796
|
-
case "
|
|
796
|
+
case "agentcache_enforce": {
|
|
797
797
|
const args = request.params.arguments;
|
|
798
798
|
const project = args.project || detectedProject;
|
|
799
799
|
const input = { tool_name: args.tool_name, tool_input: args.tool_input || {} };
|
|
800
800
|
const result = evaluatePolicy(input, repo.getEnforcedRules(project));
|
|
801
801
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
802
802
|
}
|
|
803
|
-
case "
|
|
803
|
+
case "agentcache_save_observation": {
|
|
804
804
|
const args = request.params.arguments;
|
|
805
805
|
const project = args.project || detectedProject;
|
|
806
806
|
const scope = args.scope || defaultScope(args.type);
|
|
@@ -849,7 +849,7 @@ async function startMcpServer() {
|
|
|
849
849
|
});
|
|
850
850
|
return { content: [{ type: "text", text: JSON.stringify({ saved: true, scope }) }] };
|
|
851
851
|
}
|
|
852
|
-
case "
|
|
852
|
+
case "agentcache_get_knowledge": {
|
|
853
853
|
const args = request.params.arguments || {};
|
|
854
854
|
const project = args.project || detectedProject;
|
|
855
855
|
const items = repo.getKnowledgeItems(project, {
|
|
@@ -860,7 +860,7 @@ async function startMcpServer() {
|
|
|
860
860
|
const summary = filtered.map((i) => `[${i.id}] [${i.scope}/${i.confidence}] (${i.type}) ${i.content}`).join("\n");
|
|
861
861
|
return { content: [{ type: "text", text: summary || "No knowledge items found." }] };
|
|
862
862
|
}
|
|
863
|
-
case "
|
|
863
|
+
case "agentcache_deprecate_knowledge": {
|
|
864
864
|
const args = request.params.arguments;
|
|
865
865
|
const item = repo.getKnowledgeItem(args.id);
|
|
866
866
|
if (!item) {
|
|
@@ -2,20 +2,22 @@ import {
|
|
|
2
2
|
findProjectRoot,
|
|
3
3
|
getClaudeTranscriptsDir,
|
|
4
4
|
getContinueSessionsDir,
|
|
5
|
+
getDataDir,
|
|
5
6
|
getDbPath,
|
|
6
|
-
getGlobalLoopDir,
|
|
7
7
|
getProjectDisplayName,
|
|
8
8
|
getProjectId,
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
isInitialized,
|
|
10
|
+
migrateFromLegacy
|
|
11
|
+
} from "./chunk-WHP4Z32Z.js";
|
|
11
12
|
import "./chunk-MLKGABMK.js";
|
|
12
13
|
export {
|
|
13
14
|
findProjectRoot,
|
|
14
15
|
getClaudeTranscriptsDir,
|
|
15
16
|
getContinueSessionsDir,
|
|
17
|
+
getDataDir,
|
|
16
18
|
getDbPath,
|
|
17
|
-
getGlobalLoopDir,
|
|
18
19
|
getProjectDisplayName,
|
|
19
20
|
getProjectId,
|
|
20
|
-
|
|
21
|
+
isInitialized,
|
|
22
|
+
migrateFromLegacy
|
|
21
23
|
};
|
package/dist/postinstall.js
CHANGED
|
@@ -2,11 +2,12 @@ import {
|
|
|
2
2
|
detectInstalledIdes,
|
|
3
3
|
registerClaudeHooks,
|
|
4
4
|
registerMcpServer
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LDQPTAZ7.js";
|
|
6
6
|
import {
|
|
7
|
+
getDataDir,
|
|
7
8
|
getDbPath,
|
|
8
|
-
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
migrateFromLegacy
|
|
10
|
+
} from "./chunk-WHP4Z32Z.js";
|
|
10
11
|
import {
|
|
11
12
|
SqliteKnowledgeRepository
|
|
12
13
|
} from "./chunk-MMSMDJ4O.js";
|
|
@@ -18,7 +19,8 @@ if (process.env.CI) {
|
|
|
18
19
|
process.exit(0);
|
|
19
20
|
}
|
|
20
21
|
try {
|
|
21
|
-
|
|
22
|
+
migrateFromLegacy();
|
|
23
|
+
mkdirSync(getDataDir(), { recursive: true });
|
|
22
24
|
const repo = new SqliteKnowledgeRepository(getDbPath());
|
|
23
25
|
repo.close();
|
|
24
26
|
const ides = detectInstalledIdes().filter((i) => i.detected);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
evaluatePolicy
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-T7BJPANN.js";
|
|
4
4
|
import {
|
|
5
5
|
findProjectRoot,
|
|
6
6
|
getDbPath,
|
|
7
7
|
getProjectId,
|
|
8
|
-
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
isInitialized
|
|
9
|
+
} from "./chunk-WHP4Z32Z.js";
|
|
10
10
|
import {
|
|
11
11
|
SqliteKnowledgeRepository
|
|
12
12
|
} from "./chunk-MMSMDJ4O.js";
|
|
@@ -14,7 +14,7 @@ import "./chunk-MLKGABMK.js";
|
|
|
14
14
|
|
|
15
15
|
// src/hooks/pre-tool-use.ts
|
|
16
16
|
function handlePreToolUse(input) {
|
|
17
|
-
if (!
|
|
17
|
+
if (!isInitialized()) return {};
|
|
18
18
|
const projectRoot = findProjectRoot();
|
|
19
19
|
const repo = new SqliteKnowledgeRepository(getDbPath());
|
|
20
20
|
try {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
findAllClaudeTranscripts,
|
|
3
3
|
findAllContinueTranscripts
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-OSFK44XC.js";
|
|
5
5
|
import {
|
|
6
6
|
getDbPath,
|
|
7
7
|
getProjectId,
|
|
8
|
-
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
isInitialized
|
|
9
|
+
} from "./chunk-WHP4Z32Z.js";
|
|
10
10
|
import {
|
|
11
11
|
SqliteKnowledgeRepository
|
|
12
12
|
} from "./chunk-MMSMDJ4O.js";
|
|
@@ -25,7 +25,7 @@ function inferProjectRootFromTranscriptPath(path) {
|
|
|
25
25
|
return dir;
|
|
26
26
|
}
|
|
27
27
|
async function handleSessionStart() {
|
|
28
|
-
if (!
|
|
28
|
+
if (!isInitialized()) return;
|
|
29
29
|
const repo = new SqliteKnowledgeRepository(getDbPath());
|
|
30
30
|
const compiledPaths = new Set(repo.getAllCompiledTranscriptPaths());
|
|
31
31
|
const allTranscripts = [
|
|
@@ -2,11 +2,12 @@ import {
|
|
|
2
2
|
detectInstalledIdes,
|
|
3
3
|
registerClaudeHooks,
|
|
4
4
|
registerMcpServer
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LDQPTAZ7.js";
|
|
6
6
|
import {
|
|
7
|
+
getDataDir,
|
|
7
8
|
getDbPath,
|
|
8
|
-
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
migrateFromLegacy
|
|
10
|
+
} from "./chunk-WHP4Z32Z.js";
|
|
10
11
|
import {
|
|
11
12
|
SqliteKnowledgeRepository
|
|
12
13
|
} from "./chunk-MMSMDJ4O.js";
|
|
@@ -15,13 +16,14 @@ import "./chunk-MLKGABMK.js";
|
|
|
15
16
|
// src/setup.ts
|
|
16
17
|
import { mkdirSync } from "fs";
|
|
17
18
|
async function runSetup() {
|
|
18
|
-
|
|
19
|
+
migrateFromLegacy();
|
|
20
|
+
mkdirSync(getDataDir(), { recursive: true });
|
|
19
21
|
const repo = new SqliteKnowledgeRepository(getDbPath());
|
|
20
22
|
repo.close();
|
|
21
23
|
const ides = detectInstalledIdes();
|
|
22
24
|
const detected = ides.filter((i) => i.detected);
|
|
23
25
|
console.log(`
|
|
24
|
-
|
|
26
|
+
AgentCache setup complete.`);
|
|
25
27
|
console.log(` Central DB: ${getDbPath()}
|
|
26
28
|
`);
|
|
27
29
|
if (detected.length === 0) {
|
|
@@ -40,7 +42,7 @@ Loop setup complete.`);
|
|
|
40
42
|
Claude Code hooks: registered (Stop, SessionStart, PreToolUse)`);
|
|
41
43
|
}
|
|
42
44
|
console.log(`
|
|
43
|
-
Done.
|
|
45
|
+
Done. AgentCache compiles knowledge across all sessions and IDEs.`);
|
|
44
46
|
}
|
|
45
47
|
export {
|
|
46
48
|
runSetup
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
findLatestTranscript
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OSFK44XC.js";
|
|
4
4
|
import {
|
|
5
5
|
findProjectRoot,
|
|
6
6
|
getDbPath,
|
|
7
7
|
getProjectId,
|
|
8
|
-
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
isInitialized
|
|
9
|
+
} from "./chunk-WHP4Z32Z.js";
|
|
10
10
|
import {
|
|
11
11
|
SqliteKnowledgeRepository
|
|
12
12
|
} from "./chunk-MMSMDJ4O.js";
|
|
@@ -15,7 +15,7 @@ import "./chunk-MLKGABMK.js";
|
|
|
15
15
|
// src/hooks/stop.ts
|
|
16
16
|
import { randomUUID } from "crypto";
|
|
17
17
|
async function handleStop(payload) {
|
|
18
|
-
if (!
|
|
18
|
+
if (!isInitialized()) return;
|
|
19
19
|
const transcriptPath = payload?.transcript_path || findLatestTranscript();
|
|
20
20
|
if (!transcriptPath) return;
|
|
21
21
|
const repo = new SqliteKnowledgeRepository(getDbPath());
|
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
|
-
};
|