apsolut-cortex 0.1.1 → 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/dist/cli.js CHANGED
@@ -1,335 +1,32 @@
1
1
  #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __export = (target, all) => {
4
- for (var name in all)
5
- __defProp(target, name, {
6
- get: all[name],
7
- enumerable: true,
8
- configurable: true,
9
- set: (newValue) => all[name] = () => newValue
10
- });
11
- };
12
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
13
-
14
- // src/db.ts
15
- var exports_db = {};
16
- __export(exports_db, {
17
- upsertSession: () => upsertSession,
18
- upsertProject: () => upsertProject,
19
- updateWeight: () => updateWeight,
20
- searchVector: () => searchVector,
21
- searchBM25: () => searchBM25,
22
- mergeRRF: () => mergeRRF,
23
- markObservationsPromoted: () => markObservationsPromoted,
24
- insertObservation: () => insertObservation,
25
- insertMemory: () => insertMemory,
26
- getSessionObservations: () => getSessionObservations,
27
- getRecentSummaries: () => getRecentSummaries,
28
- getDb: () => getDb,
29
- decayAndPrune: () => decayAndPrune,
30
- cosineSimilarity: () => cosineSimilarity,
31
- applyMMR: () => applyMMR,
32
- REGISTRY_PATH: () => REGISTRY_PATH,
33
- MODELS_DIR: () => MODELS_DIR,
34
- DB_PATH: () => DB_PATH,
35
- CORTEX_DIR: () => CORTEX_DIR
36
- });
37
- import Database from "better-sqlite3";
38
- import { existsSync, mkdirSync } from "fs";
39
- import { homedir } from "os";
40
- import { join } from "path";
41
- function getDb() {
42
- if (_db)
43
- return _db;
44
- if (!existsSync(CORTEX_DIR))
45
- mkdirSync(CORTEX_DIR, { recursive: true });
46
- if (!existsSync(MODELS_DIR))
47
- mkdirSync(MODELS_DIR, { recursive: true });
48
- _db = new Database(DB_PATH);
49
- _db.pragma("journal_mode = WAL");
50
- _db.pragma("synchronous = NORMAL");
51
- _db.pragma("foreign_keys = ON");
52
- _db.pragma("cache_size = -32000");
53
- _db.exec(`
54
- CREATE TABLE IF NOT EXISTS projects (
55
- id TEXT PRIMARY KEY,
56
- name TEXT NOT NULL,
57
- path TEXT,
58
- created_at INTEGER NOT NULL,
59
- last_session INTEGER
60
- );
61
-
62
- CREATE TABLE IF NOT EXISTS sessions (
63
- id TEXT PRIMARY KEY,
64
- project_id TEXT NOT NULL REFERENCES projects(id),
65
- started_at INTEGER NOT NULL,
66
- ended_at INTEGER,
67
- summary TEXT,
68
- memories_injected INTEGER NOT NULL DEFAULT 0,
69
- memories_stored INTEGER NOT NULL DEFAULT 0,
70
- tool_failures INTEGER NOT NULL DEFAULT 0
71
- );
72
-
73
- CREATE INDEX IF NOT EXISTS idx_sessions_project
74
- ON sessions(project_id, started_at DESC);
75
-
76
- CREATE TABLE IF NOT EXISTS observations (
77
- id TEXT PRIMARY KEY,
78
- session_id TEXT NOT NULL REFERENCES sessions(id),
79
- project_id TEXT NOT NULL,
80
- tool_name TEXT,
81
- content TEXT NOT NULL,
82
- category TEXT,
83
- created_at INTEGER NOT NULL,
84
- promoted INTEGER NOT NULL DEFAULT 0
85
- );
86
-
87
- CREATE INDEX IF NOT EXISTS idx_obs_session ON observations(session_id);
88
- CREATE INDEX IF NOT EXISTS idx_obs_project ON observations(project_id, created_at DESC);
89
-
90
- CREATE TABLE IF NOT EXISTS memories (
91
- id TEXT PRIMARY KEY,
92
- project_id TEXT NOT NULL,
93
- tier TEXT NOT NULL DEFAULT 'semantic',
94
- category TEXT NOT NULL DEFAULT 'insight',
95
- trust TEXT NOT NULL DEFAULT 'observed',
96
- content TEXT NOT NULL,
97
- context TEXT,
98
- source TEXT NOT NULL DEFAULT 'manual',
99
- embedding BLOB,
100
- weight REAL NOT NULL DEFAULT 1.0,
101
- used_count INTEGER NOT NULL DEFAULT 0,
102
- last_used INTEGER,
103
- created_at INTEGER NOT NULL,
104
- session_id TEXT REFERENCES sessions(id),
105
- flagged INTEGER NOT NULL DEFAULT 0,
106
- flag_reason TEXT
107
- );
108
-
109
- CREATE INDEX IF NOT EXISTS idx_mem_project ON memories(project_id);
110
- CREATE INDEX IF NOT EXISTS idx_mem_weight ON memories(project_id, weight DESC);
111
- CREATE INDEX IF NOT EXISTS idx_mem_tier ON memories(project_id, tier);
112
- CREATE INDEX IF NOT EXISTS idx_mem_trust ON memories(project_id, trust);
113
- CREATE INDEX IF NOT EXISTS idx_mem_flagged ON memories(project_id, flagged)
114
- WHERE flagged = 1;
115
-
116
- CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
117
- content, context,
118
- content='memories',
119
- content_rowid='rowid',
120
- tokenize='porter ascii'
121
- );
122
-
123
- CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
124
- INSERT INTO memories_fts(rowid, content, context)
125
- VALUES (new.rowid, new.content, COALESCE(new.context, ''));
126
- END;
127
-
128
- CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
129
- INSERT INTO memories_fts(memories_fts, rowid, content, context)
130
- VALUES ('delete', old.rowid, old.content, COALESCE(old.context, ''));
131
- INSERT INTO memories_fts(rowid, content, context)
132
- VALUES (new.rowid, new.content, COALESCE(new.context, ''));
133
- END;
134
-
135
- CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
136
- INSERT INTO memories_fts(memories_fts, rowid, content, context)
137
- VALUES ('delete', old.rowid, old.content, COALESCE(old.context, ''));
138
- END;
139
- `);
140
- return _db;
141
- }
142
- function upsertProject(db, project) {
143
- const existing = db.prepare("SELECT id FROM projects WHERE id = ?").get(project.id);
144
- if (existing) {
145
- db.prepare("UPDATE projects SET last_session = ? WHERE id = ?").run(Date.now(), project.id);
146
- } else {
147
- db.prepare("INSERT INTO projects (id, name, path, created_at) VALUES (?, ?, ?, ?)").run(project.id, project.name, project.path ?? null, Date.now());
148
- }
149
- }
150
- function upsertSession(db, s) {
151
- const existing = db.prepare("SELECT id FROM sessions WHERE id = ?").get(s.id);
152
- if (existing) {
153
- const sets = [];
154
- const vals = [];
155
- if (s.ended_at !== undefined) {
156
- sets.push("ended_at = ?");
157
- vals.push(s.ended_at);
158
- }
159
- if (s.summary !== undefined) {
160
- sets.push("summary = ?");
161
- vals.push(s.summary);
162
- }
163
- if (s.memories_stored !== undefined) {
164
- sets.push("memories_stored = ?");
165
- vals.push(s.memories_stored);
166
- }
167
- if (s.tool_failures !== undefined) {
168
- sets.push("tool_failures = ?");
169
- vals.push(s.tool_failures);
170
- }
171
- if (sets.length) {
172
- vals.push(s.id);
173
- db.prepare(`UPDATE sessions SET ${sets.join(", ")} WHERE id = ?`).run(...vals);
174
- }
175
- } else {
176
- db.prepare("INSERT INTO sessions (id, project_id, started_at) VALUES (?, ?, ?)").run(s.id, s.project_id, Date.now());
177
- }
178
- }
179
- function getRecentSummaries(db, projectId, limit = 3) {
180
- const rows = db.prepare(`
181
- SELECT summary FROM sessions
182
- WHERE project_id = ? AND summary IS NOT NULL AND summary != ''
183
- ORDER BY started_at DESC LIMIT ?
184
- `).all(projectId, limit);
185
- return rows.map((r) => r.summary);
186
- }
187
- function insertObservation(db, obs) {
188
- db.prepare(`
189
- INSERT INTO observations (id, session_id, project_id, tool_name, content, category, created_at)
190
- VALUES (?, ?, ?, ?, ?, ?, ?)
191
- `).run(crypto.randomUUID(), obs.session_id, obs.project_id, obs.tool_name ?? null, obs.content, obs.category ?? null, Date.now());
192
- }
193
- function getSessionObservations(db, sessionId) {
194
- return db.prepare("SELECT tool_name, content, category FROM observations WHERE session_id = ? AND promoted = 0 ORDER BY created_at ASC").all(sessionId);
195
- }
196
- function markObservationsPromoted(db, sessionId) {
197
- db.prepare("UPDATE observations SET promoted = 1 WHERE session_id = ?").run(sessionId);
198
- }
199
- function insertMemory(db, m) {
200
- const id = crypto.randomUUID();
201
- db.prepare(`
202
- INSERT INTO memories
203
- (id, project_id, tier, category, trust, content, context,
204
- source, embedding, weight, used_count, created_at, session_id)
205
- VALUES
206
- (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)
207
- `).run(id, m.project_id, m.tier, m.category, m.trust, m.content, m.context ?? null, m.source, m.embedding ?? null, m.weight, Date.now(), m.session_id ?? null);
208
- return id;
209
- }
210
- function searchBM25(db, projectId, query, limit) {
211
- return db.prepare(`
212
- SELECT m.* FROM memories_fts
213
- JOIN memories m ON m.rowid = memories_fts.rowid
214
- WHERE memories_fts MATCH ? AND m.project_id = ?
215
- ORDER BY bm25(memories_fts) LIMIT ?
216
- `).all(query, projectId, limit);
217
- }
218
- function cosineSimilarity(a, b) {
219
- let dot = 0, na = 0, nb = 0;
220
- for (let i = 0;i < a.length; i++) {
221
- dot += a[i] * b[i];
222
- na += a[i] * a[i];
223
- nb += b[i] * b[i];
224
- }
225
- const d = Math.sqrt(na) * Math.sqrt(nb);
226
- return d === 0 ? 0 : dot / d;
227
- }
228
- function searchVector(db, projectId, queryEmb, limit) {
229
- const candidates = db.prepare("SELECT * FROM memories WHERE project_id = ? AND embedding IS NOT NULL").all(projectId);
230
- return candidates.map((m) => ({
231
- ...m,
232
- similarity: cosineSimilarity(queryEmb, new Float32Array(m.embedding.buffer))
233
- })).sort((a, b) => b.similarity - a.similarity).slice(0, limit);
234
- }
235
- function mergeRRF(list1, list2, limit, allItems) {
236
- const k = 60;
237
- const scores = new Map;
238
- list1.forEach((r, i) => scores.set(r.id, (scores.get(r.id) ?? 0) + 1 / (i + k)));
239
- list2.forEach((r, i) => scores.set(r.id, (scores.get(r.id) ?? 0) + 1 / (i + k)));
240
- return [...scores.entries()].sort(([, a], [, b]) => b - a).slice(0, limit).map(([id]) => allItems.get(id)).filter(Boolean);
241
- }
242
- function applyMMR(candidates, queryEmb, limit, lambda = 0.7) {
243
- if (!queryEmb || candidates.length <= limit)
244
- return candidates.slice(0, limit);
245
- const selected = [];
246
- const remaining = [...candidates];
247
- while (selected.length < limit && remaining.length > 0) {
248
- let bestIdx = 0;
249
- let bestScore = -Infinity;
250
- for (let i = 0;i < remaining.length; i++) {
251
- const cand = remaining[i];
252
- const candEmb = cand.embedding ? new Float32Array(cand.embedding.buffer) : null;
253
- if (!candEmb) {
254
- bestIdx = i;
255
- break;
256
- }
257
- const relevance = cand.similarity ?? cosineSimilarity(queryEmb, candEmb);
258
- const maxSim = selected.reduce((max, s) => {
259
- if (!s.embedding)
260
- return max;
261
- const sim = cosineSimilarity(candEmb, new Float32Array(s.embedding.buffer));
262
- return Math.max(max, sim);
263
- }, 0);
264
- const score = lambda * relevance - (1 - lambda) * maxSim;
265
- if (score > bestScore) {
266
- bestScore = score;
267
- bestIdx = i;
268
- }
269
- }
270
- selected.push(remaining[bestIdx]);
271
- remaining.splice(bestIdx, 1);
272
- }
273
- return selected;
274
- }
275
- function updateWeight(db, id, score) {
276
- const mem = db.prepare("SELECT weight, used_count FROM memories WHERE id = ?").get(id);
277
- if (!mem)
278
- return;
279
- const alpha = 0.3;
280
- const newWeight = alpha * (score / 3) + (1 - alpha) * mem.weight;
281
- const newTrust = newWeight > 1.4 || mem.used_count + 1 >= 3 ? "validated" : undefined;
282
- if (newTrust) {
283
- db.prepare("UPDATE memories SET weight = ?, used_count = used_count + 1, last_used = ?, trust = CASE WHEN trust = 'observed' THEN ? ELSE trust END WHERE id = ?").run(newWeight, Date.now(), newTrust, id);
284
- } else {
285
- db.prepare("UPDATE memories SET weight = ?, used_count = used_count + 1, last_used = ? WHERE id = ?").run(newWeight, Date.now(), id);
286
- }
287
- }
288
- function decayAndPrune(db, projectId) {
289
- const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
290
- const decayed = db.prepare(`
291
- UPDATE memories
292
- SET weight = weight * CASE
293
- WHEN trust IN ('proven', 'canonical') THEN 1.0
294
- WHEN trust = 'validated' THEN 0.98
295
- ELSE 0.95
296
- END
297
- WHERE project_id = ?
298
- AND trust NOT IN ('canonical')
299
- AND (last_used IS NULL OR last_used < ?)
300
- `).run(projectId, cutoff).changes;
301
- const pruned = db.prepare(`
302
- DELETE FROM memories
303
- WHERE project_id = ? AND weight < 0.1 AND used_count > 3
304
- AND trust NOT IN ('proven', 'canonical')
305
- `).run(projectId).changes;
306
- return { decayed, pruned };
307
- }
308
- var CORTEX_DIR, DB_PATH, REGISTRY_PATH, MODELS_DIR, _db = null;
309
- var init_db = __esm(() => {
310
- CORTEX_DIR = join(homedir(), ".apsolut");
311
- DB_PATH = join(CORTEX_DIR, "memory.db");
312
- REGISTRY_PATH = join(CORTEX_DIR, "registry.json");
313
- MODELS_DIR = join(CORTEX_DIR, "models");
314
- });
315
2
 
316
3
  // src/cli.ts
317
4
  import {
318
- existsSync as existsSync3,
319
- mkdirSync as mkdirSync3,
5
+ existsSync as existsSync2,
6
+ mkdirSync as mkdirSync2,
320
7
  readFileSync as readFileSync2,
321
8
  writeFileSync as writeFileSync2
322
9
  } from "fs";
323
10
  import { join as join2, resolve, dirname as dirname2 } from "path";
324
11
  import { homedir as homedir2 } from "os";
325
- import { fileURLToPath } from "url";
12
+ import { fileURLToPath, pathToFileURL } from "url";
326
13
 
327
14
  // src/registry.ts
328
- init_db();
329
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
15
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
330
16
  import { dirname } from "path";
17
+
18
+ // src/db.ts
19
+ import Database from "better-sqlite3";
20
+ import { homedir } from "os";
21
+ import { join } from "path";
22
+ var CORTEX_DIR = join(homedir(), ".apsolut");
23
+ var DB_PATH = join(CORTEX_DIR, "memory.db");
24
+ var REGISTRY_PATH = join(CORTEX_DIR, "registry.json");
25
+ var MODELS_DIR = join(CORTEX_DIR, "models");
26
+
27
+ // src/registry.ts
331
28
  function readRegistry() {
332
- if (!existsSync2(REGISTRY_PATH))
29
+ if (!existsSync(REGISTRY_PATH))
333
30
  return { projects: {} };
334
31
  try {
335
32
  return JSON.parse(readFileSync(REGISTRY_PATH, "utf-8"));
@@ -339,8 +36,8 @@ function readRegistry() {
339
36
  }
340
37
  function writeRegistry(reg) {
341
38
  const dir = dirname(REGISTRY_PATH);
342
- if (!existsSync2(dir))
343
- mkdirSync2(dir, { recursive: true });
39
+ if (!existsSync(dir))
40
+ mkdirSync(dir, { recursive: true });
344
41
  writeFileSync(REGISTRY_PATH, JSON.stringify(reg, null, 2));
345
42
  }
346
43
  function registerProject(id, name, path) {
@@ -354,6 +51,7 @@ var __filename2 = fileURLToPath(import.meta.url);
354
51
  var __dirname2 = dirname2(__filename2);
355
52
  var PACKAGE_ROOT = resolve(__dirname2, "..");
356
53
  var IS_DIST = __dirname2.endsWith("dist") || __dirname2.includes(`${process.sep}dist${process.sep}`);
54
+ var PKG_VERSION = JSON.parse(readFileSync2(join2(PACKAGE_ROOT, "package.json"), "utf-8")).version;
357
55
  var PROJECT_ROOT = process.cwd();
358
56
  var CLAUDE_SETTINGS = join2(homedir2(), ".claude", "settings.json");
359
57
  var MCP_JSON = join2(PROJECT_ROOT, ".mcp.json");
@@ -384,7 +82,7 @@ switch (cmd) {
384
82
  break;
385
83
  default:
386
84
  console.log(`
387
- apsolut-cortex v0.1.0
85
+ apsolut-cortex v${PKG_VERSION}
388
86
 
389
87
  Commands:
390
88
  init Set up memory for this Claude Code project
@@ -397,23 +95,23 @@ Models: ~/.apsolut/models/
397
95
  }
398
96
  async function runHook(name) {
399
97
  const hookPath = IS_DIST ? join2(__dirname2, "hooks", `${name}.js`) : join2(__dirname2, "hooks", `${name}.ts`);
400
- if (!existsSync3(hookPath)) {
98
+ if (!existsSync2(hookPath)) {
401
99
  process.stderr.write(`[apsolut-cortex] hook not found: ${hookPath}
402
100
  `);
403
101
  process.exit(0);
404
102
  }
405
- await import(hookPath);
103
+ await import(pathToFileURL(hookPath).href);
406
104
  }
407
105
  async function init() {
408
106
  console.log(`
409
107
  apsolut-cortex init
410
108
  `);
411
- if (!existsSync3(PROJECT_APSOLUT)) {
412
- mkdirSync3(PROJECT_APSOLUT, { recursive: true });
109
+ if (!existsSync2(PROJECT_APSOLUT)) {
110
+ mkdirSync2(PROJECT_APSOLUT, { recursive: true });
413
111
  }
414
112
  let projectId;
415
113
  let projectName;
416
- if (existsSync3(PROJECT_CONFIG)) {
114
+ if (existsSync2(PROJECT_CONFIG)) {
417
115
  const existing = JSON.parse(readFileSync2(PROJECT_CONFIG, "utf-8"));
418
116
  projectId = existing.id;
419
117
  projectName = existing.name;
@@ -436,7 +134,7 @@ apsolut-cortex init
436
134
  const mcpCommand = IS_DIST ? "node" : "bun";
437
135
  const mcpArgs = [mcpServerPath];
438
136
  let mcp = {};
439
- if (existsSync3(MCP_JSON)) {
137
+ if (existsSync2(MCP_JSON)) {
440
138
  try {
441
139
  mcp = JSON.parse(readFileSync2(MCP_JSON, "utf-8"));
442
140
  } catch {}
@@ -452,16 +150,16 @@ apsolut-cortex init
452
150
  console.log(`✓ Written .mcp.json`);
453
151
  const hookCmd = IS_DIST ? "apsolut-cortex" : `bun run ${join2(__dirname2, "cli.ts")}`;
454
152
  const hookEntries = {
455
- SessionStart: [{ command: `${hookCmd} hook:session-start` }],
456
- PostToolUse: [{ command: `${hookCmd} hook:post-tool-use` }],
457
- Stop: [{ command: `${hookCmd} hook:stop` }],
458
- SessionEnd: [{ command: `${hookCmd} hook:session-end` }]
153
+ SessionStart: [{ matcher: "", hooks: [{ type: "command", command: `${hookCmd} hook:session-start` }] }],
154
+ PostToolUse: [{ matcher: "", hooks: [{ type: "command", command: `${hookCmd} hook:post-tool-use` }] }],
155
+ Stop: [{ matcher: "", hooks: [{ type: "command", command: `${hookCmd} hook:stop` }] }],
156
+ SessionEnd: [{ matcher: "", hooks: [{ type: "command", command: `${hookCmd} hook:session-end` }] }]
459
157
  };
460
158
  let settings = {};
461
159
  const settingsDir = dirname2(CLAUDE_SETTINGS);
462
- if (!existsSync3(settingsDir))
463
- mkdirSync3(settingsDir, { recursive: true });
464
- if (existsSync3(CLAUDE_SETTINGS)) {
160
+ if (!existsSync2(settingsDir))
161
+ mkdirSync2(settingsDir, { recursive: true });
162
+ if (existsSync2(CLAUDE_SETTINGS)) {
465
163
  try {
466
164
  settings = JSON.parse(readFileSync2(CLAUDE_SETTINGS, "utf-8"));
467
165
  } catch {}
@@ -480,7 +178,7 @@ apsolut-cortex init
480
178
  writeFileSync2(CLAUDE_SETTINGS, JSON.stringify(settings, null, 2));
481
179
  console.log(added > 0 ? `✓ Registered ${added} hooks in ~/.claude/settings.json` : `✓ Hooks already registered`);
482
180
  const gitignore = join2(PROJECT_ROOT, ".gitignore");
483
- if (existsSync3(gitignore)) {
181
+ if (existsSync2(gitignore)) {
484
182
  const content = readFileSync2(gitignore, "utf-8");
485
183
  if (!content.includes(".apsolut/")) {
486
184
  writeFileSync2(gitignore, content + `
@@ -500,7 +198,7 @@ apsolut-cortex init
500
198
  │ ██║ ██║██║ ███████║╚██████╔╝███████╗╚██████╔╝ ██║ │
501
199
  │ ╚═╝ ╚═╝╚═╝ ╚══════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ │
502
200
  │ │
503
- │ c o r t e x · v 0 . 1 . 0
201
+ │ c o r t e x · v ${PKG_VERSION}
504
202
  │ │
505
203
  └─────────────────────────────────────────────────────────┘
506
204
 
@@ -518,13 +216,13 @@ apsolut-cortex init
518
216
  console.log(BANNER);
519
217
  }
520
218
  async function status() {
521
- const { getDb: getDb2 } = await Promise.resolve().then(() => (init_db(), exports_db));
522
- if (!existsSync3(PROJECT_CONFIG)) {
219
+ const { getDb } = await import(pathToFileURL(join2(__dirname2, "db.js")).href);
220
+ if (!existsSync2(PROJECT_CONFIG)) {
523
221
  console.log("No project found. Run: apsolut-cortex init");
524
222
  process.exit(1);
525
223
  }
526
224
  const project = JSON.parse(readFileSync2(PROJECT_CONFIG, "utf-8"));
527
- const db = getDb2();
225
+ const db = getDb();
528
226
  const total = db.prepare("SELECT COUNT(*) as n FROM memories WHERE project_id = ?").get(project.id).n;
529
227
  const sessions = db.prepare("SELECT COUNT(*) as n FROM sessions WHERE project_id = ?").get(project.id).n;
530
228
  const byTrust = db.prepare(`
@@ -554,7 +252,7 @@ DB: ~/.apsolut/memory.db
554
252
  `);
555
253
  }
556
254
  function uninstall() {
557
- if (existsSync3(MCP_JSON)) {
255
+ if (existsSync2(MCP_JSON)) {
558
256
  try {
559
257
  const mcp = JSON.parse(readFileSync2(MCP_JSON, "utf-8"));
560
258
  if (mcp.mcpServers?.["apsolut-cortex"]) {
@@ -564,14 +262,21 @@ function uninstall() {
564
262
  }
565
263
  } catch {}
566
264
  }
567
- if (existsSync3(CLAUDE_SETTINGS)) {
265
+ if (existsSync2(CLAUDE_SETTINGS)) {
568
266
  try {
569
267
  const settings = JSON.parse(readFileSync2(CLAUDE_SETTINGS, "utf-8"));
570
268
  const hooks = settings.hooks;
571
269
  if (hooks) {
572
270
  for (const event of ["SessionStart", "PostToolUse", "Stop", "SessionEnd"]) {
573
271
  if (hooks[event]) {
574
- hooks[event] = hooks[event].filter((e) => !(typeof e === "object" && e.command?.includes("apsolut-cortex")));
272
+ hooks[event] = hooks[event].filter((e) => {
273
+ if (typeof e !== "object")
274
+ return true;
275
+ if (Array.isArray(e.hooks)) {
276
+ return !e.hooks.some((h) => h.command?.includes("apsolut-cortex"));
277
+ }
278
+ return !e.command?.includes("apsolut-cortex");
279
+ });
575
280
  }
576
281
  }
577
282
  writeFileSync2(CLAUDE_SETTINGS, JSON.stringify(settings, null, 2));
@@ -1,6 +1,3 @@
1
- #!/usr/bin/env bun
2
- // @bun
3
-
4
1
  // src/hooks/post-tool-use.ts
5
2
  import { readFileSync, existsSync as existsSync2 } from "fs";
6
3
  import { join as join2 } from "path";
@@ -190,7 +187,12 @@ function classifyToolUse(toolName, toolInput, toolResponse) {
190
187
 
191
188
  // src/hooks/post-tool-use.ts
192
189
  async function main() {
193
- const raw = await Bun.stdin.text();
190
+ const raw = await new Promise((resolve) => {
191
+ let d = "";
192
+ process.stdin.setEncoding("utf-8");
193
+ process.stdin.on("data", (c) => d += c);
194
+ process.stdin.on("end", () => resolve(d));
195
+ });
194
196
  let data = {};
195
197
  try {
196
198
  data = JSON.parse(raw);
@@ -1,9 +1,6 @@
1
- #!/usr/bin/env bun
2
- // @bun
3
-
4
1
  // src/hooks/session-end.ts
5
2
  import { readFileSync, existsSync as existsSync2 } from "fs";
6
- import { join as join3 } from "path";
3
+ import { join as join2 } from "path";
7
4
 
8
5
  // src/db.ts
9
6
  import Database from "better-sqlite3";
@@ -324,18 +321,7 @@ async function compressSession(observations, project) {
324
321
 
325
322
  // src/embed.ts
326
323
  import { pipeline, env } from "@xenova/transformers";
327
-
328
- // src/db.ts
329
- import Database2 from "better-sqlite3";
330
- import { homedir as homedir2 } from "os";
331
- import { join as join2 } from "path";
332
- var CORTEX_DIR2 = join2(homedir2(), ".apsolut");
333
- var DB_PATH2 = join2(CORTEX_DIR2, "memory.db");
334
- var REGISTRY_PATH2 = join2(CORTEX_DIR2, "registry.json");
335
- var MODELS_DIR2 = join2(CORTEX_DIR2, "models");
336
-
337
- // src/embed.ts
338
- env.cacheDir = MODELS_DIR2;
324
+ env.cacheDir = MODELS_DIR;
339
325
  env.allowRemoteModels = true;
340
326
  var _embedder = null;
341
327
  async function getEmbedder() {
@@ -355,7 +341,12 @@ function float32ToBuffer(arr) {
355
341
 
356
342
  // src/hooks/session-end.ts
357
343
  async function main() {
358
- const raw = await Bun.stdin.text();
344
+ const raw = await new Promise((resolve) => {
345
+ let d = "";
346
+ process.stdin.setEncoding("utf-8");
347
+ process.stdin.on("data", (c) => d += c);
348
+ process.stdin.on("end", () => resolve(d));
349
+ });
359
350
  let data = {};
360
351
  try {
361
352
  data = JSON.parse(raw);
@@ -364,7 +355,7 @@ async function main() {
364
355
  }
365
356
  const cwd = data.cwd ?? process.cwd();
366
357
  const sessionId = data.session_id ?? "unknown";
367
- const projectFile = join3(cwd, ".apsolut", "project.json");
358
+ const projectFile = join2(cwd, ".apsolut", "project.json");
368
359
  if (!existsSync2(projectFile))
369
360
  process.exit(0);
370
361
  let project = null;
@@ -1,6 +1,3 @@
1
- #!/usr/bin/env bun
2
- // @bun
3
-
4
1
  // src/hooks/session-start.ts
5
2
  import { readFileSync, existsSync as existsSync2 } from "fs";
6
3
  import { join as join2 } from "path";
@@ -156,7 +153,12 @@ function upsertSession(db, s) {
156
153
 
157
154
  // src/hooks/session-start.ts
158
155
  async function main() {
159
- const raw = await Bun.stdin.text();
156
+ const raw = await new Promise((resolve) => {
157
+ let d = "";
158
+ process.stdin.setEncoding("utf-8");
159
+ process.stdin.on("data", (c) => d += c);
160
+ process.stdin.on("end", () => resolve(d));
161
+ });
160
162
  let data = {};
161
163
  try {
162
164
  data = JSON.parse(raw);
@@ -180,7 +182,7 @@ async function main() {
180
182
  const db = getDb();
181
183
  upsertProject(db, { id: project.id, name: project.name, path: cwd });
182
184
  upsertSession(db, { id: sessionId, project_id: project.id });
183
- process.stdout.write(`[apsolut-cortex] Project: ${project.name} \u2014 say "remember <topic>" to search memory.`);
185
+ process.stdout.write(`[apsolut-cortex] Project: ${project.name} say "remember <topic>" to search memory.`);
184
186
  } catch {
185
187
  process.exit(0);
186
188
  }
@@ -1,6 +1,3 @@
1
- #!/usr/bin/env bun
2
- // @bun
3
-
4
1
  // src/hooks/stop.ts
5
2
  import { readFileSync, existsSync as existsSync2 } from "fs";
6
3
  import { join as join2 } from "path";
@@ -158,7 +155,7 @@ var CORRECTION_PATTERNS = [
158
155
  /(?:my mistake|i was wrong|incorrect)[^.]*[.!]\s*(.{20,150})/gi,
159
156
  /(?:wait|oops)[,.]?\s+(.{20,150})/gi,
160
157
  /(?:turns? out|it seems?)\s+(.{20,150})/gi,
161
- /(?:should(?:n'?t)? have|shouldn'?t).{0,30}[\u2014\u2013-]\s*(.{20,150})/gi,
158
+ /(?:should(?:n'?t)? have|shouldn'?t).{0,30}[—–-]\s*(.{20,150})/gi,
162
159
  /(?:the correct|correct(?:ly)?)\s+(?:way|path|file|approach)\s+is\s+(.{20,150})/gi
163
160
  ];
164
161
  function extractCorrections(transcript) {
@@ -175,7 +172,12 @@ function extractCorrections(transcript) {
175
172
  return [...new Set(found)].slice(0, 5);
176
173
  }
177
174
  async function main() {
178
- const raw = await Bun.stdin.text();
175
+ const raw = await new Promise((resolve) => {
176
+ let d = "";
177
+ process.stdin.setEncoding("utf-8");
178
+ process.stdin.on("data", (c) => d += c);
179
+ process.stdin.on("end", () => resolve(d));
180
+ });
179
181
  let data = {};
180
182
  try {
181
183
  data = JSON.parse(raw);
@@ -9,7 +9,8 @@ import {
9
9
  ListToolsRequestSchema
10
10
  } from "@modelcontextprotocol/sdk/types.js";
11
11
  import { readFileSync, existsSync as existsSync2 } from "fs";
12
- import { join as join3 } from "path";
12
+ import { join as join3, dirname, resolve } from "path";
13
+ import { fileURLToPath } from "url";
13
14
 
14
15
  // src/db.ts
15
16
  import Database from "better-sqlite3";
@@ -264,7 +265,9 @@ var db = getDb();
264
265
  if (project?.id) {
265
266
  upsertProject(db, { id: project.id, name: project.name, path: PROJECT_PATH });
266
267
  }
267
- var server = new Server({ name: "apsolut-cortex", version: "0.1.0" }, { capabilities: { tools: {} } });
268
+ var __mcp_dirname = dirname(fileURLToPath(import.meta.url));
269
+ var PKG_VERSION = JSON.parse(readFileSync(resolve(__mcp_dirname, "..", "package.json"), "utf-8")).version;
270
+ var server = new Server({ name: "apsolut-cortex", version: PKG_VERSION }, { capabilities: { tools: {} } });
268
271
  function requireProject() {
269
272
  if (!project?.id)
270
273
  throw new Error("No project found. Run: apsolut-cortex init");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apsolut-cortex",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Persistent memory for Claude Code projects — stores corrections, decisions, and patterns across sessions",
5
5
  "type": "module",
6
6
  "bin": {