agentcache 0.2.3 → 0.3.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.
@@ -0,0 +1,461 @@
1
+ import {
2
+ processClustering,
3
+ processExtraction,
4
+ startCompile
5
+ } from "./chunk-IER56P6A.js";
6
+ import {
7
+ acquireLock,
8
+ releaseLock
9
+ } from "./chunk-RXBTPJVW.js";
10
+ import {
11
+ findAllClaudeTranscripts,
12
+ findAllCodexTranscripts,
13
+ findAllContinueTranscripts,
14
+ findAllRooCodeTranscripts,
15
+ getGooseDbPath,
16
+ parseTranscript
17
+ } from "./chunk-TE6DBLJ5.js";
18
+ import {
19
+ getDbPath,
20
+ getProjectId,
21
+ isInitialized
22
+ } from "./chunk-WHP4Z32Z.js";
23
+ import {
24
+ SqliteKnowledgeRepository
25
+ } from "./chunk-MMSMDJ4O.js";
26
+ import {
27
+ __esm,
28
+ __export,
29
+ __require,
30
+ __toCommonJS
31
+ } from "./chunk-KFQGP6VL.js";
32
+
33
+ // src/utils/transcript-parsers/goose-sqlite.ts
34
+ var goose_sqlite_exports = {};
35
+ __export(goose_sqlite_exports, {
36
+ canParse: () => canParse,
37
+ getGooseDbPath: () => getGooseDbPath2,
38
+ hasGooseSessions: () => hasGooseSessions,
39
+ parse: () => parse,
40
+ parseSession: () => parseSession
41
+ });
42
+ import { existsSync } from "fs";
43
+ import { join } from "path";
44
+ import { homedir } from "os";
45
+ function canParse(path) {
46
+ return path.endsWith("goose-sessions.db") || path.includes("goose/sessions/sessions.db");
47
+ }
48
+ function getGooseDbPath2() {
49
+ return join(homedir(), ".local", "share", "goose", "sessions", "sessions.db");
50
+ }
51
+ function hasGooseSessions() {
52
+ return existsSync(getGooseDbPath2());
53
+ }
54
+ function parseSession(db, sessionId) {
55
+ const events = [];
56
+ const rows = db.prepare("SELECT role, content_json FROM messages WHERE session_id = ? ORDER BY created_timestamp ASC").all(sessionId);
57
+ for (const row of rows) {
58
+ try {
59
+ const content = JSON.parse(row.content_json);
60
+ if (!Array.isArray(content)) continue;
61
+ for (const block of content) {
62
+ if (row.role === "user" && block.type === "text" && block.text) {
63
+ events.push({ type: "message", role: "user", content: block.text });
64
+ } else if (row.role === "assistant" && block.type === "text" && block.text) {
65
+ events.push({ type: "message", role: "assistant", content: block.text });
66
+ } else if (row.role === "assistant" && block.type === "toolRequest") {
67
+ events.push({
68
+ type: "tool_use",
69
+ tool_name: block.toolCall?.value?.name || "unknown",
70
+ tool_input: block.toolCall?.value?.arguments ? { arguments: block.toolCall.value.arguments } : {}
71
+ });
72
+ }
73
+ }
74
+ } catch {
75
+ continue;
76
+ }
77
+ }
78
+ return events;
79
+ }
80
+ function parse(_path) {
81
+ return [];
82
+ }
83
+ var init_goose_sqlite = __esm({
84
+ "src/utils/transcript-parsers/goose-sqlite.ts"() {
85
+ "use strict";
86
+ }
87
+ });
88
+
89
+ // src/compile-all.ts
90
+ import { spawnSync } from "child_process";
91
+ import { existsSync as existsSync2, writeFileSync, unlinkSync } from "fs";
92
+ import { tmpdir } from "os";
93
+ import { join as join2 } from "path";
94
+ import { randomUUID } from "crypto";
95
+ function detectBackend() {
96
+ const backends = [
97
+ {
98
+ cmd: "claude",
99
+ name: "Claude Code",
100
+ buildInvoke: () => (prompt) => {
101
+ const result = spawnSync("claude", ["-p", "-", "--output-format", "text"], {
102
+ input: prompt,
103
+ encoding: "utf-8",
104
+ timeout: 12e4,
105
+ maxBuffer: 10 * 1024 * 1024
106
+ });
107
+ return result.status === 0 ? result.stdout : null;
108
+ }
109
+ },
110
+ {
111
+ cmd: "codex",
112
+ name: "Codex",
113
+ buildInvoke: () => (prompt) => {
114
+ const tmpFile = join2(tmpdir(), `agentcache-prompt-${Date.now()}.txt`);
115
+ writeFileSync(tmpFile, prompt);
116
+ const result = spawnSync("codex", ["exec", "-", "--skip-git-repo-check"], {
117
+ input: prompt,
118
+ encoding: "utf-8",
119
+ timeout: 12e4,
120
+ maxBuffer: 10 * 1024 * 1024
121
+ });
122
+ try {
123
+ unlinkSync(tmpFile);
124
+ } catch {
125
+ }
126
+ return result.status === 0 ? result.stdout : null;
127
+ }
128
+ },
129
+ {
130
+ cmd: "gemini",
131
+ name: "Gemini CLI",
132
+ buildInvoke: () => (prompt) => {
133
+ const result = spawnSync("gemini", ["-p", "-"], {
134
+ input: prompt,
135
+ encoding: "utf-8",
136
+ timeout: 12e4,
137
+ maxBuffer: 10 * 1024 * 1024
138
+ });
139
+ return result.status === 0 ? result.stdout : null;
140
+ }
141
+ },
142
+ {
143
+ cmd: "copilot",
144
+ name: "Copilot CLI",
145
+ buildInvoke: () => (prompt) => {
146
+ const result = spawnSync("copilot", ["-p", "-"], {
147
+ input: prompt,
148
+ encoding: "utf-8",
149
+ timeout: 12e4,
150
+ maxBuffer: 10 * 1024 * 1024
151
+ });
152
+ return result.status === 0 ? result.stdout : null;
153
+ }
154
+ },
155
+ {
156
+ cmd: "aider",
157
+ name: "Aider",
158
+ buildInvoke: () => (prompt) => {
159
+ const tmpFile = join2(tmpdir(), `agentcache-prompt-${Date.now()}.txt`);
160
+ writeFileSync(tmpFile, prompt);
161
+ const result = spawnSync("aider", ["--message-file", tmpFile, "--yes", "--no-stream", "--no-git"], {
162
+ encoding: "utf-8",
163
+ timeout: 12e4,
164
+ maxBuffer: 10 * 1024 * 1024
165
+ });
166
+ try {
167
+ unlinkSync(tmpFile);
168
+ } catch {
169
+ }
170
+ return result.status === 0 ? result.stdout : null;
171
+ }
172
+ },
173
+ {
174
+ cmd: "goose",
175
+ name: "Goose",
176
+ buildInvoke: () => (prompt) => {
177
+ const tmpFile = join2(tmpdir(), `agentcache-prompt-${Date.now()}.txt`);
178
+ writeFileSync(tmpFile, prompt);
179
+ const result = spawnSync("goose", ["run", "--instructions", tmpFile], {
180
+ encoding: "utf-8",
181
+ timeout: 12e4,
182
+ maxBuffer: 10 * 1024 * 1024
183
+ });
184
+ try {
185
+ unlinkSync(tmpFile);
186
+ } catch {
187
+ }
188
+ return result.status === 0 ? result.stdout : null;
189
+ }
190
+ }
191
+ ];
192
+ for (const b of backends) {
193
+ try {
194
+ const which = spawnSync("which", [b.cmd], { encoding: "utf-8" });
195
+ if (which.status === 0 && which.stdout.trim()) {
196
+ return { name: b.name, invoke: b.buildInvoke() };
197
+ }
198
+ } catch {
199
+ }
200
+ }
201
+ try {
202
+ const ollamaCheck = spawnSync("curl", ["-s", "http://localhost:11434/api/tags"], {
203
+ encoding: "utf-8",
204
+ timeout: 3e3
205
+ });
206
+ if (ollamaCheck.status === 0 && ollamaCheck.stdout.includes("models")) {
207
+ const models = JSON.parse(ollamaCheck.stdout)?.models || [];
208
+ const model = models.find((m) => /qwen|llama|mistral|gemma/i.test(m.name))?.name || models[0]?.name;
209
+ if (model) {
210
+ return {
211
+ name: `Ollama (${model})`,
212
+ invoke: (prompt) => {
213
+ const result = spawnSync("curl", [
214
+ "-s",
215
+ "http://localhost:11434/api/generate",
216
+ "-d",
217
+ JSON.stringify({ model, prompt, stream: false })
218
+ ], { encoding: "utf-8", timeout: 12e4, maxBuffer: 10 * 1024 * 1024 });
219
+ if (result.status !== 0) return null;
220
+ try {
221
+ return JSON.parse(result.stdout)?.response || null;
222
+ } catch {
223
+ return null;
224
+ }
225
+ }
226
+ };
227
+ }
228
+ }
229
+ } catch {
230
+ }
231
+ const anthropicKey = process.env.ANTHROPIC_API_KEY;
232
+ if (anthropicKey) {
233
+ return {
234
+ name: "Anthropic API (env)",
235
+ invoke: (prompt) => {
236
+ const result = spawnSync("curl", [
237
+ "-s",
238
+ "https://api.anthropic.com/v1/messages",
239
+ "-H",
240
+ "content-type: application/json",
241
+ "-H",
242
+ `x-api-key: ${anthropicKey}`,
243
+ "-H",
244
+ "anthropic-version: 2023-06-01",
245
+ "-d",
246
+ JSON.stringify({
247
+ model: "claude-sonnet-4-20250514",
248
+ max_tokens: 4096,
249
+ messages: [{ role: "user", content: prompt }]
250
+ })
251
+ ], { encoding: "utf-8", timeout: 12e4, maxBuffer: 10 * 1024 * 1024 });
252
+ if (result.status !== 0) return null;
253
+ try {
254
+ return JSON.parse(result.stdout)?.content?.[0]?.text || null;
255
+ } catch {
256
+ return null;
257
+ }
258
+ }
259
+ };
260
+ }
261
+ const openaiKey = process.env.OPENAI_API_KEY;
262
+ if (openaiKey) {
263
+ return {
264
+ name: "OpenAI API (env)",
265
+ invoke: (prompt) => {
266
+ const result = spawnSync("curl", [
267
+ "-s",
268
+ "https://api.openai.com/v1/chat/completions",
269
+ "-H",
270
+ "content-type: application/json",
271
+ "-H",
272
+ `Authorization: Bearer ${openaiKey}`,
273
+ "-d",
274
+ JSON.stringify({
275
+ model: "gpt-4o-mini",
276
+ messages: [{ role: "user", content: prompt }]
277
+ })
278
+ ], { encoding: "utf-8", timeout: 12e4, maxBuffer: 10 * 1024 * 1024 });
279
+ if (result.status !== 0) return null;
280
+ try {
281
+ return JSON.parse(result.stdout)?.choices?.[0]?.message?.content || null;
282
+ } catch {
283
+ return null;
284
+ }
285
+ }
286
+ };
287
+ }
288
+ return null;
289
+ }
290
+ function discoverAllTranscripts(repo) {
291
+ const compiledPaths = new Set(repo.getAllCompiledTranscriptPaths());
292
+ const results = [];
293
+ const allPaths = [
294
+ ...findAllClaudeTranscripts(),
295
+ ...findAllContinueTranscripts(),
296
+ ...findAllCodexTranscripts(),
297
+ ...findAllRooCodeTranscripts()
298
+ ];
299
+ for (const path of allPaths) {
300
+ if (compiledPaths.has(path)) continue;
301
+ const projectRoot = inferProjectRoot(path);
302
+ const project = getProjectId(projectRoot);
303
+ results.push({ path, project, projectRoot });
304
+ }
305
+ return results;
306
+ }
307
+ function inferProjectRoot(path) {
308
+ if (path.includes(".claude/projects/")) {
309
+ const slug = path.split(".claude/projects/")[1]?.split("/")[0] || "";
310
+ if (slug.startsWith("-")) return slug.replace(/-/g, "/");
311
+ }
312
+ if (path.includes(".codex/sessions/")) return process.cwd();
313
+ if (path.includes("roo-cline/tasks/")) return process.cwd();
314
+ return process.cwd();
315
+ }
316
+ function processOneTranscript(repo, path, project, projectRoot, backend) {
317
+ const events = parseTranscript(path);
318
+ if (events.length < 3) return { created: 0, reinforced: 0, skipped: true };
319
+ const sessionId = `sess_${randomUUID().slice(0, 8)}`;
320
+ const state = startCompile(events, sessionId, project, projectRoot, repo, path);
321
+ const extractionResponse = backend.invoke(state.prompt);
322
+ if (!extractionResponse) return { created: 0, reinforced: 0, skipped: true };
323
+ const extractResult = processExtraction(repo, extractionResponse, sessionId, project, projectRoot);
324
+ if (extractResult.status === "complete") {
325
+ return { created: 0, reinforced: 0, skipped: false };
326
+ }
327
+ const clusterResponse = backend.invoke(extractResult.clusteringPrompt);
328
+ if (!clusterResponse) return { created: 0, reinforced: 0, skipped: true };
329
+ const clusterResult = processClustering(repo, clusterResponse, sessionId, project, projectRoot);
330
+ const diag = clusterResult.diagnostics;
331
+ const createdMatch = diag.match(/(\d+) new knowledge/);
332
+ const reinforcedMatch = diag.match(/(\d+) reinforced/);
333
+ return {
334
+ created: createdMatch ? parseInt(createdMatch[1]) : 0,
335
+ reinforced: reinforcedMatch ? parseInt(reinforcedMatch[1]) : 0,
336
+ skipped: false
337
+ };
338
+ }
339
+ function processGooseSessions(repo, backend) {
340
+ const dbPath = getGooseDbPath();
341
+ if (!existsSync2(dbPath)) return { processed: 0, created: 0 };
342
+ let Database;
343
+ try {
344
+ Database = __require("better-sqlite3");
345
+ } catch {
346
+ return { processed: 0, created: 0 };
347
+ }
348
+ const gooseDb = new Database(dbPath, { readonly: true });
349
+ const compiledPaths = new Set(repo.getAllCompiledTranscriptPaths());
350
+ const sessions = gooseDb.prepare("SELECT id, working_dir FROM sessions").all();
351
+ let processed = 0;
352
+ let totalCreated = 0;
353
+ for (const session of sessions) {
354
+ const markerPath = `goose:${session.id}`;
355
+ if (compiledPaths.has(markerPath)) continue;
356
+ const { parseSession: parseSession2 } = (init_goose_sqlite(), __toCommonJS(goose_sqlite_exports));
357
+ const events = parseSession2(gooseDb, session.id);
358
+ if (events.length < 3) continue;
359
+ const projectRoot = session.working_dir || process.cwd();
360
+ const project = getProjectId(projectRoot);
361
+ const sessionId = `sess_${randomUUID().slice(0, 8)}`;
362
+ const state = startCompile(events, sessionId, project, projectRoot, repo, markerPath);
363
+ const extractionResponse = backend.invoke(state.prompt);
364
+ if (!extractionResponse) continue;
365
+ const extractResult = processExtraction(repo, extractionResponse, sessionId, project, projectRoot);
366
+ if (extractResult.status === "needs_clustering") {
367
+ const clusterResponse = backend.invoke(extractResult.clusteringPrompt);
368
+ if (clusterResponse) {
369
+ processClustering(repo, clusterResponse, sessionId, project, projectRoot);
370
+ }
371
+ }
372
+ processed++;
373
+ totalCreated++;
374
+ }
375
+ gooseDb.close();
376
+ return { processed, created: totalCreated };
377
+ }
378
+ async function runCompileAll() {
379
+ if (!isInitialized()) {
380
+ console.error("AgentCache not initialized. Run: npm install -g agentcache");
381
+ process.exit(1);
382
+ }
383
+ if (!acquireLock()) {
384
+ console.error("Another compile-all process is already running. Exiting.");
385
+ process.exit(0);
386
+ }
387
+ process.on("exit", releaseLock);
388
+ process.on("SIGINT", () => {
389
+ releaseLock();
390
+ process.exit(130);
391
+ });
392
+ process.on("SIGTERM", () => {
393
+ releaseLock();
394
+ process.exit(143);
395
+ });
396
+ const backend = detectBackend();
397
+ if (!backend) {
398
+ releaseLock();
399
+ console.error("No LLM backend found. Install one of: claude, codex, gemini, copilot, aider, goose");
400
+ process.exit(1);
401
+ }
402
+ console.log(`AgentCache compile-all`);
403
+ console.log(`LLM backend: ${backend.name}`);
404
+ console.log("");
405
+ const repo = new SqliteKnowledgeRepository(getDbPath());
406
+ const transcripts = discoverAllTranscripts(repo);
407
+ const gooseAvailable = existsSync2(getGooseDbPath());
408
+ const total = transcripts.length + (gooseAvailable ? 1 : 0);
409
+ if (total === 0 && !gooseAvailable) {
410
+ console.log("No uncompiled transcripts found. Knowledge is up to date.");
411
+ repo.close();
412
+ return;
413
+ }
414
+ const estimatedMinutes = Math.ceil(transcripts.length * 0.7);
415
+ console.log(`Found ${transcripts.length} transcripts to process`);
416
+ if (gooseAvailable) console.log(`+ Goose sessions available`);
417
+ console.log(`Estimated time: ~${estimatedMinutes} minutes`);
418
+ console.log(`Started: ${(/* @__PURE__ */ new Date()).toLocaleTimeString()}`);
419
+ console.log("\u2500".repeat(50));
420
+ let processed = 0;
421
+ let totalCreated = 0;
422
+ let totalReinforced = 0;
423
+ let errors = 0;
424
+ for (const t of transcripts) {
425
+ processed++;
426
+ const label = t.path.split("/").slice(-2).join("/");
427
+ process.stdout.write(`[${processed}/${transcripts.length}] ${label.slice(0, 40)}... `);
428
+ try {
429
+ const result = processOneTranscript(repo, t.path, t.project, t.projectRoot, backend);
430
+ if (result.skipped) {
431
+ console.log("skipped");
432
+ } else {
433
+ totalCreated += result.created;
434
+ totalReinforced += result.reinforced;
435
+ console.log(`+${result.created} new, ${result.reinforced} reinforced`);
436
+ }
437
+ } catch (err) {
438
+ errors++;
439
+ console.log(`error: ${err.message?.slice(0, 50)}`);
440
+ }
441
+ }
442
+ if (gooseAvailable) {
443
+ process.stdout.write("Processing Goose sessions... ");
444
+ try {
445
+ const gooseResult = processGooseSessions(repo, backend);
446
+ console.log(`${gooseResult.processed} sessions processed`);
447
+ } catch (err) {
448
+ console.log(`error: ${err.message?.slice(0, 50)}`);
449
+ }
450
+ }
451
+ repo.close();
452
+ console.log("\u2500".repeat(50));
453
+ console.log(`Done: ${(/* @__PURE__ */ new Date()).toLocaleTimeString()}`);
454
+ console.log(` ${processed} transcripts processed`);
455
+ console.log(` ${totalCreated} knowledge items created`);
456
+ console.log(` ${totalReinforced} reinforced`);
457
+ if (errors > 0) console.log(` ${errors} errors`);
458
+ }
459
+ export {
460
+ runCompileAll
461
+ };