agentcache 0.3.4 → 0.4.1
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 +151 -221
- package/dist/3-canonicalizer-HIN2F7SZ.js +11 -0
- package/dist/{chunk-OXHITHDC.js → chunk-CUBZRYS5.js} +21 -125
- package/dist/chunk-GGAATZKM.js +120 -0
- package/dist/{chunk-QVQJPJGX.js → chunk-IGCH7SZT.js} +1 -27
- package/dist/{chunk-VFE4SDMO.js → chunk-JUDLOBOC.js} +6 -3
- package/dist/{chunk-H3S3HDHK.js → chunk-JVLMZU5I.js} +6 -8
- package/dist/{chunk-ZVDODLZ7.js → chunk-PSASDZQE.js} +24 -3
- package/dist/{chunk-S4GSIEKL.js → chunk-T4COG3XD.js} +1 -0
- package/dist/cli.js +249 -19
- package/dist/{compile-all-LB5S67BQ.js → compile-all-7ESDEBFG.js} +36 -14
- package/dist/mcp.js +68 -22
- package/dist/{paths-ULP2T4HZ.js → paths-5LZRKNYY.js} +1 -1
- package/dist/postinstall.js +27 -10
- package/dist/{pre-tool-use-UBJFRHCW.js → pre-tool-use-7F7NTHCS.js} +2 -2
- package/dist/{session-start-SUR6FXRD.js → session-start-EIHYCS3J.js} +3 -3
- package/dist/{setup-TVNRAAK3.js → setup-45BVUDXN.js} +3 -3
- package/dist/{sqlite-MP6SRBBQ.js → sqlite-NM2BVHUY.js} +1 -1
- package/dist/{stop-HFZZ2LFA.js → stop-TPCRE7RE.js} +3 -6
- package/package.json +1 -1
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.1");
|
|
7
7
|
program.command("setup").description("Detect IDEs and register AgentCache (runs automatically on install)").action(async () => {
|
|
8
|
-
const { runSetup } = await import("./setup-
|
|
8
|
+
const { runSetup } = await import("./setup-45BVUDXN.js");
|
|
9
9
|
await runSetup();
|
|
10
10
|
});
|
|
11
11
|
program.command("serve").description("Start AgentCache MCP server (spawned by IDEs automatically)").action(async () => {
|
|
@@ -13,49 +13,270 @@ program.command("serve").description("Start AgentCache MCP server (spawned by ID
|
|
|
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-HFZZ2LFA.js");
|
|
17
|
-
let payload;
|
|
18
16
|
try {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
const { handleStop } = await import("./stop-TPCRE7RE.js");
|
|
18
|
+
let payload;
|
|
19
|
+
try {
|
|
20
|
+
let data = "";
|
|
21
|
+
for await (const chunk of process.stdin) {
|
|
22
|
+
data += chunk;
|
|
23
|
+
}
|
|
24
|
+
if (data.trim()) {
|
|
25
|
+
payload = JSON.parse(data);
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
22
28
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
29
|
+
await handleStop(payload);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
process.stderr.write(`agentcache compile-session: ${err.message}
|
|
32
|
+
`);
|
|
27
33
|
}
|
|
28
|
-
await handleStop(payload);
|
|
29
34
|
});
|
|
30
35
|
program.command("discover").description("SessionStart hook: discover uncompiled transcripts").action(async () => {
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
try {
|
|
37
|
+
const { handleSessionStart } = await import("./session-start-EIHYCS3J.js");
|
|
38
|
+
await handleSessionStart();
|
|
39
|
+
} catch (err) {
|
|
40
|
+
process.stderr.write(`agentcache discover: ${err.message}
|
|
41
|
+
`);
|
|
42
|
+
}
|
|
33
43
|
});
|
|
34
44
|
program.command("enforce").description("PreToolUse hook: policy enforcement").action(async () => {
|
|
35
|
-
const { handlePreToolUse } = await import("./pre-tool-use-UBJFRHCW.js");
|
|
36
45
|
let data = "";
|
|
37
46
|
for await (const chunk of process.stdin) {
|
|
38
47
|
data += chunk;
|
|
39
48
|
}
|
|
40
49
|
try {
|
|
50
|
+
const { handlePreToolUse } = await import("./pre-tool-use-7F7NTHCS.js");
|
|
41
51
|
const input = JSON.parse(data);
|
|
42
52
|
const result = handlePreToolUse(input);
|
|
43
53
|
process.stdout.write(JSON.stringify(result));
|
|
44
|
-
} catch {
|
|
54
|
+
} catch (err) {
|
|
55
|
+
process.stderr.write(`agentcache enforce: ${err.message}
|
|
56
|
+
`);
|
|
45
57
|
process.stdout.write("{}");
|
|
46
58
|
}
|
|
47
59
|
});
|
|
60
|
+
program.command("review").description("Review quarantined observations \u2014 approve or reject before they're injected").option("--approve-all", "Approve all pending items").option("--reject-all", "Reject (archive) all pending items").action(async (opts) => {
|
|
61
|
+
const { getDbPath, isInitialized, findProjectRoot, getProjectId } = await import("./paths-5LZRKNYY.js");
|
|
62
|
+
if (!isInitialized()) {
|
|
63
|
+
console.log("AgentCache not initialized. Run: agentcache setup");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const { SqliteKnowledgeRepository } = await import("./sqlite-NM2BVHUY.js");
|
|
67
|
+
const repo = new SqliteKnowledgeRepository(getDbPath());
|
|
68
|
+
const project = getProjectId(findProjectRoot());
|
|
69
|
+
const items = repo.getQuarantinedItems(project);
|
|
70
|
+
if (items.length === 0) {
|
|
71
|
+
console.log("No quarantined items. All observations are either approved or auto-promoted.");
|
|
72
|
+
repo.close();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (opts.approveAll) {
|
|
76
|
+
for (const item of items) {
|
|
77
|
+
repo.promoteItem(item.id);
|
|
78
|
+
}
|
|
79
|
+
console.log(`Approved ${items.length} items. They will now be injected into future sessions.`);
|
|
80
|
+
repo.close();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (opts.rejectAll) {
|
|
84
|
+
for (const item of items) {
|
|
85
|
+
repo.updateKnowledgeItem(item.id, { status: "archived", updatedAt: Date.now() });
|
|
86
|
+
}
|
|
87
|
+
console.log(`Rejected ${items.length} items. They will not be injected.`);
|
|
88
|
+
repo.close();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
console.log(`${items.length} quarantined observation(s):
|
|
92
|
+
`);
|
|
93
|
+
for (const item of items) {
|
|
94
|
+
const age = Math.round((Date.now() - item.createdAt) / (1e3 * 60 * 60));
|
|
95
|
+
console.log(` [${item.id}] (${item.type}/${item.scope}) ${age}h ago`);
|
|
96
|
+
console.log(` ${item.content.slice(0, 120)}`);
|
|
97
|
+
console.log("");
|
|
98
|
+
}
|
|
99
|
+
console.log("Actions:");
|
|
100
|
+
console.log(" agentcache review --approve-all Approve all and inject into sessions");
|
|
101
|
+
console.log(" agentcache review --reject-all Archive all (won't be injected)");
|
|
102
|
+
console.log(" agentcache promote <id> Approve a specific item");
|
|
103
|
+
repo.close();
|
|
104
|
+
});
|
|
105
|
+
program.command("promote <id>").description("Promote a specific quarantined item to approved (USER authority)").action(async (id) => {
|
|
106
|
+
const { getDbPath, isInitialized } = await import("./paths-5LZRKNYY.js");
|
|
107
|
+
if (!isInitialized()) {
|
|
108
|
+
console.log("AgentCache not initialized. Run: agentcache setup");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const { SqliteKnowledgeRepository } = await import("./sqlite-NM2BVHUY.js");
|
|
112
|
+
const repo = new SqliteKnowledgeRepository(getDbPath());
|
|
113
|
+
const item = repo.getKnowledgeItem(id);
|
|
114
|
+
if (!item) {
|
|
115
|
+
console.log(`Item not found: ${id}`);
|
|
116
|
+
repo.close();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
repo.promoteItem(id);
|
|
120
|
+
console.log(`Promoted: ${item.content.slice(0, 80)}`);
|
|
121
|
+
repo.close();
|
|
122
|
+
});
|
|
123
|
+
program.command("add-rule <content>").description("Add an enforced policy rule (human-only, blocks tool calls that violate it)").option("--global", "Apply to all projects (default: current project only)").action(async (content, opts) => {
|
|
124
|
+
const { getDbPath, isInitialized, findProjectRoot, getProjectId } = await import("./paths-5LZRKNYY.js");
|
|
125
|
+
const { randomUUID } = await import("crypto");
|
|
126
|
+
if (!isInitialized()) {
|
|
127
|
+
console.log("AgentCache not initialized. Run: agentcache setup");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const { SqliteKnowledgeRepository } = await import("./sqlite-NM2BVHUY.js");
|
|
131
|
+
const { computeCanonicalHash } = await import("./3-canonicalizer-HIN2F7SZ.js");
|
|
132
|
+
const repo = new SqliteKnowledgeRepository(getDbPath());
|
|
133
|
+
const project = getProjectId(findProjectRoot());
|
|
134
|
+
const scope = opts.global ? "global" : "project";
|
|
135
|
+
repo.saveKnowledgeItem({
|
|
136
|
+
id: `ki_${randomUUID().slice(0, 8)}`,
|
|
137
|
+
canonicalHash: computeCanonicalHash(content),
|
|
138
|
+
type: "rule",
|
|
139
|
+
title: content.slice(0, 80),
|
|
140
|
+
content,
|
|
141
|
+
confidence: "high",
|
|
142
|
+
observationCount: 1,
|
|
143
|
+
authority: "USER",
|
|
144
|
+
status: "active",
|
|
145
|
+
enforce: true,
|
|
146
|
+
project,
|
|
147
|
+
scope,
|
|
148
|
+
createdAt: Date.now(),
|
|
149
|
+
updatedAt: Date.now(),
|
|
150
|
+
lastSeenAt: Date.now(),
|
|
151
|
+
metadata: { source: "cli" }
|
|
152
|
+
});
|
|
153
|
+
console.log(`Enforced rule added (${scope}): ${content}`);
|
|
154
|
+
repo.close();
|
|
155
|
+
});
|
|
156
|
+
program.command("doctor").description("Diagnose AgentCache installation and report problems").action(async () => {
|
|
157
|
+
const { existsSync, readFileSync } = await import("fs");
|
|
158
|
+
const { join } = await import("path");
|
|
159
|
+
const { homedir } = await import("os");
|
|
160
|
+
const { spawnSync } = await import("child_process");
|
|
161
|
+
const { getDataDir, getDbPath, isInitialized } = await import("./paths-5LZRKNYY.js");
|
|
162
|
+
let ok = 0;
|
|
163
|
+
let warn = 0;
|
|
164
|
+
let fail = 0;
|
|
165
|
+
function pass(msg) {
|
|
166
|
+
console.log(` \u2713 ${msg}`);
|
|
167
|
+
ok++;
|
|
168
|
+
}
|
|
169
|
+
function warning(msg) {
|
|
170
|
+
console.log(` \u26A0 ${msg}`);
|
|
171
|
+
warn++;
|
|
172
|
+
}
|
|
173
|
+
function error(msg) {
|
|
174
|
+
console.log(` \u2717 ${msg}`);
|
|
175
|
+
fail++;
|
|
176
|
+
}
|
|
177
|
+
console.log("AgentCache Doctor\n");
|
|
178
|
+
console.log("Storage:");
|
|
179
|
+
const dataDir = getDataDir();
|
|
180
|
+
if (existsSync(dataDir)) {
|
|
181
|
+
pass(`Data directory exists: ${dataDir}`);
|
|
182
|
+
} else {
|
|
183
|
+
error(`Data directory missing: ${dataDir}`);
|
|
184
|
+
}
|
|
185
|
+
const dbPath = getDbPath();
|
|
186
|
+
if (existsSync(dbPath)) {
|
|
187
|
+
try {
|
|
188
|
+
const { SqliteKnowledgeRepository } = await import("./sqlite-NM2BVHUY.js");
|
|
189
|
+
const repo = new SqliteKnowledgeRepository(dbPath);
|
|
190
|
+
repo.close();
|
|
191
|
+
pass(`Database accessible: ${dbPath}`);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
if (err.message?.includes("NODE_MODULE_VERSION") || err.message?.includes("was compiled against")) {
|
|
194
|
+
error(`Native module ABI mismatch \u2014 run: npm rebuild better-sqlite3 -g`);
|
|
195
|
+
} else {
|
|
196
|
+
error(`Database broken: ${err.message}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} else if (isInitialized()) {
|
|
200
|
+
warning("Database file missing but data directory exists");
|
|
201
|
+
} else {
|
|
202
|
+
warning("Not initialized yet \u2014 run: agentcache setup");
|
|
203
|
+
}
|
|
204
|
+
console.log("\nIDE registrations:");
|
|
205
|
+
const claudeJson = join(homedir(), ".claude.json");
|
|
206
|
+
if (existsSync(claudeJson)) {
|
|
207
|
+
try {
|
|
208
|
+
const config = JSON.parse(readFileSync(claudeJson, "utf-8"));
|
|
209
|
+
if (config.mcpServers?.agentcache) {
|
|
210
|
+
pass("Claude Code: registered");
|
|
211
|
+
} else {
|
|
212
|
+
warning("Claude Code: ~/.claude.json exists but no agentcache server");
|
|
213
|
+
}
|
|
214
|
+
} catch {
|
|
215
|
+
warning("Claude Code: ~/.claude.json unreadable");
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
warning("Claude Code: not registered");
|
|
219
|
+
}
|
|
220
|
+
const settingsPath = join(homedir(), ".claude", "settings.json");
|
|
221
|
+
if (existsSync(settingsPath)) {
|
|
222
|
+
try {
|
|
223
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
224
|
+
const perms = settings.permissions?.allow || [];
|
|
225
|
+
if (perms.some((p) => p.includes("agentcache"))) {
|
|
226
|
+
pass("Claude Code permissions: auto-approved");
|
|
227
|
+
} else {
|
|
228
|
+
warning("Claude Code permissions: not in allow list");
|
|
229
|
+
}
|
|
230
|
+
if (settings.hooks?.Stop?.some((h) => JSON.stringify(h).includes("agentcache"))) {
|
|
231
|
+
pass("Claude Code hooks: registered");
|
|
232
|
+
} else {
|
|
233
|
+
warning("Claude Code hooks: not registered");
|
|
234
|
+
}
|
|
235
|
+
} catch {
|
|
236
|
+
warning("Claude Code settings: unreadable");
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
console.log("\nLLM backends (for compile-all):");
|
|
240
|
+
const backends = ["claude", "codex", "gemini", "copilot", "aider", "goose"];
|
|
241
|
+
const found = [];
|
|
242
|
+
for (const cmd of backends) {
|
|
243
|
+
try {
|
|
244
|
+
if (spawnSync("which", [cmd], { encoding: "utf-8", timeout: 3e3 }).status === 0) {
|
|
245
|
+
found.push(cmd);
|
|
246
|
+
}
|
|
247
|
+
} catch {
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (process.env.ANTHROPIC_API_KEY) found.push("Anthropic API (env)");
|
|
251
|
+
if (process.env.OPENAI_API_KEY) found.push("OpenAI API (env)");
|
|
252
|
+
if (found.length > 0) {
|
|
253
|
+
pass(`Available: ${found.join(", ")}`);
|
|
254
|
+
} else {
|
|
255
|
+
warning("No LLM backend found \u2014 compile-all won't work");
|
|
256
|
+
}
|
|
257
|
+
console.log("\nRuntime:");
|
|
258
|
+
const nodeVersion = process.version;
|
|
259
|
+
const major = parseInt(nodeVersion.slice(1));
|
|
260
|
+
if (major >= 20) {
|
|
261
|
+
pass(`Node ${nodeVersion}`);
|
|
262
|
+
} else {
|
|
263
|
+
error(`Node ${nodeVersion} \u2014 requires >=20.12.0`);
|
|
264
|
+
}
|
|
265
|
+
console.log(`
|
|
266
|
+
${ok} passed, ${warn} warnings, ${fail} errors`);
|
|
267
|
+
if (fail > 0) process.exit(1);
|
|
268
|
+
});
|
|
48
269
|
program.command("compile-all").description("Batch-compile all unprocessed transcripts using an available LLM CLI").action(async () => {
|
|
49
|
-
const { runCompileAll } = await import("./compile-all-
|
|
270
|
+
const { runCompileAll } = await import("./compile-all-7ESDEBFG.js");
|
|
50
271
|
await runCompileAll();
|
|
51
272
|
});
|
|
52
273
|
program.command("status").description("Show AgentCache knowledge stats").action(async () => {
|
|
53
|
-
const { getDbPath, isInitialized, findProjectRoot, getProjectId, getProjectDisplayName } = await import("./paths-
|
|
274
|
+
const { getDbPath, isInitialized, findProjectRoot, getProjectId, getProjectDisplayName } = await import("./paths-5LZRKNYY.js");
|
|
54
275
|
if (!isInitialized()) {
|
|
55
276
|
console.log("AgentCache not initialized. Run: agentcache setup");
|
|
56
277
|
return;
|
|
57
278
|
}
|
|
58
|
-
const { SqliteKnowledgeRepository } = await import("./sqlite-
|
|
279
|
+
const { SqliteKnowledgeRepository } = await import("./sqlite-NM2BVHUY.js");
|
|
59
280
|
const repo = new SqliteKnowledgeRepository(getDbPath());
|
|
60
281
|
const projectRoot = findProjectRoot();
|
|
61
282
|
const project = getProjectId(projectRoot);
|
|
@@ -68,10 +289,19 @@ program.command("status").description("Show AgentCache knowledge stats").action(
|
|
|
68
289
|
const globalItems = items.filter((i) => i.scope === "global");
|
|
69
290
|
const projectItems = items.filter((i) => i.scope === "project");
|
|
70
291
|
const pending = repo.getPendingCount();
|
|
71
|
-
repo.close();
|
|
72
292
|
console.log(`AgentCache \u2014 ${displayName} (${project})`);
|
|
73
293
|
console.log(` ${items.length} items (${globalItems.length} global, ${projectItems.length} project)`);
|
|
74
294
|
console.log(` ${rules.length} rules | ${lessons.length} lessons | ${decisions.length} decisions | ${context.length} context`);
|
|
75
295
|
if (pending > 0) console.log(` ${pending} sessions pending compilation`);
|
|
296
|
+
const allProjects = repo.getProjectStats();
|
|
297
|
+
if (allProjects.length > 1) {
|
|
298
|
+
console.log("");
|
|
299
|
+
console.log("All projects:");
|
|
300
|
+
for (const p of allProjects) {
|
|
301
|
+
const marker = p.project === project ? " \u2190 current" : "";
|
|
302
|
+
console.log(` ${p.project}: ${p.count} items${marker}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
repo.close();
|
|
76
306
|
});
|
|
77
307
|
program.parse();
|
|
@@ -2,11 +2,7 @@ import {
|
|
|
2
2
|
processClustering,
|
|
3
3
|
processExtraction,
|
|
4
4
|
startCompile
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import {
|
|
7
|
-
acquireLock,
|
|
8
|
-
releaseLock
|
|
9
|
-
} from "./chunk-VFE4SDMO.js";
|
|
5
|
+
} from "./chunk-CUBZRYS5.js";
|
|
10
6
|
import {
|
|
11
7
|
findAllClaudeTranscripts,
|
|
12
8
|
findAllCodexTranscripts,
|
|
@@ -14,15 +10,21 @@ import {
|
|
|
14
10
|
findAllRooCodeTranscripts,
|
|
15
11
|
getGooseDbPath,
|
|
16
12
|
parseTranscript
|
|
17
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-IGCH7SZT.js";
|
|
14
|
+
import {
|
|
15
|
+
acquireLock,
|
|
16
|
+
releaseLock
|
|
17
|
+
} from "./chunk-JUDLOBOC.js";
|
|
18
|
+
import "./chunk-GGAATZKM.js";
|
|
18
19
|
import {
|
|
19
20
|
getDbPath,
|
|
21
|
+
getGitRoot,
|
|
20
22
|
getProjectId,
|
|
21
23
|
isInitialized
|
|
22
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-T4COG3XD.js";
|
|
23
25
|
import {
|
|
24
26
|
SqliteKnowledgeRepository
|
|
25
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-PSASDZQE.js";
|
|
26
28
|
import {
|
|
27
29
|
__esm,
|
|
28
30
|
__export,
|
|
@@ -90,7 +92,7 @@ var init_goose_sqlite = __esm({
|
|
|
90
92
|
import { spawnSync } from "child_process";
|
|
91
93
|
import { existsSync as existsSync2, writeFileSync, unlinkSync } from "fs";
|
|
92
94
|
import { tmpdir } from "os";
|
|
93
|
-
import { join as join2 } from "path";
|
|
95
|
+
import { join as join2, dirname } from "path";
|
|
94
96
|
import { randomUUID } from "crypto";
|
|
95
97
|
function detectBackend() {
|
|
96
98
|
const backends = [
|
|
@@ -304,15 +306,35 @@ function discoverAllTranscripts(repo) {
|
|
|
304
306
|
}
|
|
305
307
|
return results;
|
|
306
308
|
}
|
|
307
|
-
function inferProjectRoot(
|
|
308
|
-
if (
|
|
309
|
-
const slug =
|
|
309
|
+
function inferProjectRoot(transcriptPath) {
|
|
310
|
+
if (transcriptPath.includes(".claude/projects/")) {
|
|
311
|
+
const slug = transcriptPath.split(".claude/projects/")[1]?.split("/")[0] || "";
|
|
310
312
|
if (slug.startsWith("-")) return slug.replace(/-/g, "/");
|
|
311
313
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
+
try {
|
|
315
|
+
const events = parseTranscript(transcriptPath);
|
|
316
|
+
for (const event of events) {
|
|
317
|
+
const filePath = extractFilePath(event);
|
|
318
|
+
if (filePath) {
|
|
319
|
+
const root = getGitRoot(dirname(filePath));
|
|
320
|
+
if (root) return root;
|
|
321
|
+
return dirname(filePath);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
314
326
|
return process.cwd();
|
|
315
327
|
}
|
|
328
|
+
function extractFilePath(event) {
|
|
329
|
+
if (event.tool_input) {
|
|
330
|
+
for (const val of Object.values(event.tool_input)) {
|
|
331
|
+
if (typeof val === "string" && val.startsWith("/") && val.includes("/") && !val.includes(" ")) {
|
|
332
|
+
return val;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
316
338
|
function processOneTranscript(repo, path, project, projectRoot, backend) {
|
|
317
339
|
const events = parseTranscript(path);
|
|
318
340
|
if (events.length < 3) return { created: 0, reinforced: 0, skipped: true };
|
package/dist/mcp.js
CHANGED
|
@@ -2,27 +2,29 @@ import {
|
|
|
2
2
|
evaluatePolicy
|
|
3
3
|
} from "./chunk-T7BJPANN.js";
|
|
4
4
|
import {
|
|
5
|
-
computeCanonicalHash,
|
|
6
5
|
processClustering,
|
|
7
6
|
processExtraction,
|
|
8
7
|
startCompile
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-CUBZRYS5.js";
|
|
9
|
+
import {
|
|
10
|
+
parseTranscript
|
|
11
|
+
} from "./chunk-IGCH7SZT.js";
|
|
10
12
|
import {
|
|
11
13
|
spawnCompileAll
|
|
12
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-JUDLOBOC.js";
|
|
13
15
|
import {
|
|
14
|
-
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
computeCanonicalHash
|
|
17
|
+
} from "./chunk-GGAATZKM.js";
|
|
16
18
|
import {
|
|
17
19
|
findProjectRoot,
|
|
18
20
|
getDataDir,
|
|
19
21
|
getDbPath,
|
|
20
22
|
getProjectId,
|
|
21
23
|
isInitialized
|
|
22
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-T4COG3XD.js";
|
|
23
25
|
import {
|
|
24
26
|
SqliteKnowledgeRepository
|
|
25
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-PSASDZQE.js";
|
|
26
28
|
import "./chunk-KFQGP6VL.js";
|
|
27
29
|
|
|
28
30
|
// src/mcp.ts
|
|
@@ -90,8 +92,34 @@ function checkForUpdates() {
|
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
// src/mcp.ts
|
|
93
|
-
import { existsSync as
|
|
95
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
96
|
+
|
|
97
|
+
// src/utils/config.ts
|
|
98
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
99
|
+
import { join as join2 } from "path";
|
|
100
|
+
var DEFAULT_CONFIG = {
|
|
101
|
+
security: "auto"
|
|
102
|
+
};
|
|
103
|
+
function getConfigPath() {
|
|
104
|
+
return join2(getDataDir(), "config.json");
|
|
105
|
+
}
|
|
106
|
+
function getConfig() {
|
|
107
|
+
const path = getConfigPath();
|
|
108
|
+
if (!existsSync2(path)) return { ...DEFAULT_CONFIG };
|
|
109
|
+
try {
|
|
110
|
+
const raw = JSON.parse(readFileSync2(path, "utf-8"));
|
|
111
|
+
return { ...DEFAULT_CONFIG, ...raw };
|
|
112
|
+
} catch {
|
|
113
|
+
return { ...DEFAULT_CONFIG };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function saveConfig(config) {
|
|
117
|
+
writeFileSync2(getConfigPath(), JSON.stringify(config, null, 2), "utf-8");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/mcp.ts
|
|
94
121
|
import { randomUUID } from "crypto";
|
|
122
|
+
var PKG_VERSION = JSON.parse(readFileSync3(new URL("../package.json", import.meta.url), "utf-8")).version;
|
|
95
123
|
function defaultScope(type) {
|
|
96
124
|
return type === "rule" || type === "lesson" ? "global" : "project";
|
|
97
125
|
}
|
|
@@ -114,9 +142,20 @@ function getResolvedProjectRoot() {
|
|
|
114
142
|
function getResolvedProjectId() {
|
|
115
143
|
return getProjectId(getResolvedProjectRoot());
|
|
116
144
|
}
|
|
145
|
+
function migrateToV04() {
|
|
146
|
+
const config = getConfig();
|
|
147
|
+
if (config.migrated_v04) return;
|
|
148
|
+
try {
|
|
149
|
+
const repo = new SqliteKnowledgeRepository(getDbPath());
|
|
150
|
+
repo.grandfatherExistingItems();
|
|
151
|
+
repo.close();
|
|
152
|
+
saveConfig({ ...config, migrated_v04: true });
|
|
153
|
+
} catch {
|
|
154
|
+
}
|
|
155
|
+
}
|
|
117
156
|
async function startMcpServer() {
|
|
118
157
|
const server = new Server(
|
|
119
|
-
{ name: "agentcache", version:
|
|
158
|
+
{ name: "agentcache", version: PKG_VERSION },
|
|
120
159
|
{
|
|
121
160
|
capabilities: { tools: {} },
|
|
122
161
|
instructions: "AgentCache is your knowledge cache. At the START of every session, call inject_context to load compiled rules, lessons, decisions, and context. Submit observations INCREMENTALLY via compile_submit as you learn them \u2014 do not wait until session end."
|
|
@@ -125,6 +164,7 @@ async function startMcpServer() {
|
|
|
125
164
|
server.oninitialized = async () => {
|
|
126
165
|
await resolveRoots(server);
|
|
127
166
|
checkForUpdates();
|
|
167
|
+
migrateToV04();
|
|
128
168
|
};
|
|
129
169
|
server.setNotificationHandler(RootsListChangedNotificationSchema, async () => {
|
|
130
170
|
await resolveRoots(server);
|
|
@@ -157,8 +197,7 @@ async function startMcpServer() {
|
|
|
157
197
|
type: { type: "string", enum: ["rule", "lesson", "decision", "context"], description: "rule=standing constraint, lesson=mistake+fix, decision=arch choice+rationale, context=current state" },
|
|
158
198
|
content: { type: "string", description: "The observation content" },
|
|
159
199
|
sourceQuote: { type: "string", description: "Optional quote from conversation that triggered this" },
|
|
160
|
-
confidence: { type: "string", enum: ["high", "medium"], description: "How confident: high=explicitly stated, medium=inferred" }
|
|
161
|
-
scope: { type: "string", enum: ["global", "project"], description: "global=applies to all projects, project=this project only. Defaults: rule/lesson->global, decision/context->project" }
|
|
200
|
+
confidence: { type: "string", enum: ["high", "medium"], description: "How confident: high=explicitly stated, medium=inferred" }
|
|
162
201
|
},
|
|
163
202
|
required: ["type", "content", "confidence"]
|
|
164
203
|
}
|
|
@@ -219,13 +258,12 @@ async function startMcpServer() {
|
|
|
219
258
|
},
|
|
220
259
|
{
|
|
221
260
|
name: "save_observation",
|
|
222
|
-
description: "Save a single observation immediately with USER authority (never overwritten by compiler). Use for important rules or decisions
|
|
261
|
+
description: "Save a single observation immediately with USER authority (never overwritten by compiler). Use for important rules or decisions the user explicitly states.",
|
|
223
262
|
inputSchema: {
|
|
224
263
|
type: "object",
|
|
225
264
|
properties: {
|
|
226
265
|
type: { type: "string", enum: ["rule", "lesson", "decision", "context"] },
|
|
227
266
|
content: { type: "string", description: "The observation content" },
|
|
228
|
-
enforce: { type: "boolean", description: "If true, this rule will BLOCK tool calls that violate it" },
|
|
229
267
|
scope: { type: "string", enum: ["global", "project"], description: "Defaults: rule/lesson->global, decision/context->project" },
|
|
230
268
|
project: { type: "string", description: "Project identifier. Auto-detected if omitted." }
|
|
231
269
|
},
|
|
@@ -272,7 +310,8 @@ async function startMcpServer() {
|
|
|
272
310
|
case "inject_context": {
|
|
273
311
|
const args = request.params.arguments || {};
|
|
274
312
|
const project = args.project || detectedProject;
|
|
275
|
-
const
|
|
313
|
+
const securityMode = getConfig().security;
|
|
314
|
+
const items = repo.getKnowledgeForContext(project, { userOnly: securityMode === "review" });
|
|
276
315
|
const rules = items.filter((i) => i.type === "rule").slice(0, 20);
|
|
277
316
|
const lessons = items.filter((i) => i.type === "lesson").slice(0, 10);
|
|
278
317
|
const decisions = items.filter((i) => i.type === "decision").slice(0, 10);
|
|
@@ -310,18 +349,25 @@ ${pendingCount} sessions pending compilation (background compiler already runnin
|
|
|
310
349
|
<!-- ${pendingCount} session(s) pending compilation (below threshold, will process when backlog grows). -->
|
|
311
350
|
`;
|
|
312
351
|
}
|
|
313
|
-
|
|
352
|
+
const quarantined = repo.getQuarantinedItems(project);
|
|
353
|
+
if (quarantined.length > 0) {
|
|
354
|
+
output += `
|
|
355
|
+
---
|
|
356
|
+
${quarantined.length} observation(s) pending review \u2014 run \`agentcache review\` to approve or they'll auto-promote when seen again.
|
|
357
|
+
`;
|
|
358
|
+
}
|
|
359
|
+
output += "\n---\nIMPORTANT: Use save_observation for decisions the user explicitly states (injected immediately).\nUse compile_submit for patterns you infer (quarantined until confirmed in a second session).\nDo NOT wait until the end \u2014 sessions can terminate without warning.\n";
|
|
314
360
|
return { content: [{ type: "text", text: output.trim() }] };
|
|
315
361
|
}
|
|
316
362
|
case "compile_submit": {
|
|
363
|
+
const securityMode = getConfig().security;
|
|
364
|
+
if (securityMode === "locked") {
|
|
365
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "compile_submit disabled \u2014 security mode is 'locked'. Use compile-all for batch processing." }) }], isError: true };
|
|
366
|
+
}
|
|
317
367
|
const args = request.params.arguments;
|
|
318
368
|
const project = args.project || detectedProject;
|
|
319
369
|
const sessionId = `sess_${randomUUID().slice(0, 8)}`;
|
|
320
|
-
const
|
|
321
|
-
...o,
|
|
322
|
-
scope: o.scope || defaultScope(o.type)
|
|
323
|
-
}));
|
|
324
|
-
const responseText = JSON.stringify({ observations: observationsWithScope });
|
|
370
|
+
const responseText = JSON.stringify({ observations: args.observations });
|
|
325
371
|
startCompile([], sessionId, project, projectRoot, repo);
|
|
326
372
|
const result = processExtraction(repo, responseText, sessionId, project, projectRoot);
|
|
327
373
|
if (result.status === "complete") {
|
|
@@ -344,7 +390,7 @@ ${pendingCount} sessions pending compilation (background compiler already runnin
|
|
|
344
390
|
if (!entry) {
|
|
345
391
|
return { content: [{ type: "text", text: JSON.stringify({ message: "No pending sessions to compile." }) }] };
|
|
346
392
|
}
|
|
347
|
-
if (!
|
|
393
|
+
if (!existsSync3(entry.transcriptPath)) {
|
|
348
394
|
return { content: [{ type: "text", text: JSON.stringify({ message: `Transcript not found: ${entry.transcriptPath}, skipped.` }) }] };
|
|
349
395
|
}
|
|
350
396
|
const events = parseTranscript(entry.transcriptPath);
|
|
@@ -402,7 +448,7 @@ ${pendingCount} sessions pending compilation (background compiler already runnin
|
|
|
402
448
|
observationCount: 1,
|
|
403
449
|
authority: "USER",
|
|
404
450
|
status: "active",
|
|
405
|
-
enforce:
|
|
451
|
+
enforce: false,
|
|
406
452
|
project,
|
|
407
453
|
scope,
|
|
408
454
|
createdAt: Date.now(),
|
package/dist/postinstall.js
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
2
|
spawnCompileAll
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JUDLOBOC.js";
|
|
4
4
|
import {
|
|
5
5
|
detectInstalledIdes,
|
|
6
6
|
registerClaudeHooks,
|
|
7
7
|
registerMcpServer
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-JVLMZU5I.js";
|
|
9
9
|
import {
|
|
10
10
|
getDataDir,
|
|
11
11
|
getDbPath,
|
|
12
12
|
migrateFromLegacy
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-T4COG3XD.js";
|
|
14
14
|
import {
|
|
15
15
|
SqliteKnowledgeRepository
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-PSASDZQE.js";
|
|
17
17
|
import "./chunk-KFQGP6VL.js";
|
|
18
18
|
|
|
19
19
|
// src/postinstall.ts
|
|
20
20
|
import { mkdirSync } from "fs";
|
|
21
|
+
import { join } from "path";
|
|
22
|
+
import { homedir } from "os";
|
|
23
|
+
import { spawnSync } from "child_process";
|
|
21
24
|
if (process.env.CI) {
|
|
22
25
|
process.exit(0);
|
|
23
26
|
}
|
|
@@ -33,15 +36,29 @@ try {
|
|
|
33
36
|
registered.push(ide.name);
|
|
34
37
|
}
|
|
35
38
|
}
|
|
39
|
+
mkdirSync(join(homedir(), ".claude"), { recursive: true });
|
|
36
40
|
registerClaudeHooks();
|
|
37
41
|
if (registered.length > 0) {
|
|
38
|
-
console.
|
|
42
|
+
console.log(`agentcache: registered with ${registered.join(", ")}`);
|
|
39
43
|
}
|
|
40
|
-
console.
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
console.log("agentcache: ready. Knowledge compiles automatically across all sessions.");
|
|
45
|
+
const hasBackend = ["claude", "codex", "gemini", "copilot", "aider", "goose"].some((cmd) => {
|
|
46
|
+
try {
|
|
47
|
+
return spawnSync("which", [cmd], { encoding: "utf-8", timeout: 3e3 }).status === 0;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}) || process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY;
|
|
52
|
+
if (hasBackend) {
|
|
53
|
+
const spawned = spawnCompileAll();
|
|
54
|
+
if (spawned) {
|
|
55
|
+
console.log("agentcache: background compilation started for existing transcripts.");
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
console.log("agentcache: no LLM backend detected for batch compilation.");
|
|
59
|
+
console.log(" Install one of: claude, codex, gemini, copilot, aider, goose");
|
|
60
|
+
console.log(" Or set ANTHROPIC_API_KEY / OPENAI_API_KEY. Knowledge compiles via MCP in the meantime.");
|
|
44
61
|
}
|
|
45
62
|
} catch (err) {
|
|
46
|
-
console.
|
|
63
|
+
console.log(`agentcache postinstall: ${err.message}. Run 'agentcache setup' manually.`);
|
|
47
64
|
}
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
getDbPath,
|
|
7
7
|
getProjectId,
|
|
8
8
|
isInitialized
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-T4COG3XD.js";
|
|
10
10
|
import {
|
|
11
11
|
SqliteKnowledgeRepository
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-PSASDZQE.js";
|
|
13
13
|
import "./chunk-KFQGP6VL.js";
|
|
14
14
|
|
|
15
15
|
// src/hooks/pre-tool-use.ts
|