apsolut-cortex 0.1.2 → 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) {
@@ -398,23 +95,23 @@ Models: ~/.apsolut/models/
398
95
  }
399
96
  async function runHook(name) {
400
97
  const hookPath = IS_DIST ? join2(__dirname2, "hooks", `${name}.js`) : join2(__dirname2, "hooks", `${name}.ts`);
401
- if (!existsSync3(hookPath)) {
98
+ if (!existsSync2(hookPath)) {
402
99
  process.stderr.write(`[apsolut-cortex] hook not found: ${hookPath}
403
100
  `);
404
101
  process.exit(0);
405
102
  }
406
- await import(hookPath);
103
+ await import(pathToFileURL(hookPath).href);
407
104
  }
408
105
  async function init() {
409
106
  console.log(`
410
107
  apsolut-cortex init
411
108
  `);
412
- if (!existsSync3(PROJECT_APSOLUT)) {
413
- mkdirSync3(PROJECT_APSOLUT, { recursive: true });
109
+ if (!existsSync2(PROJECT_APSOLUT)) {
110
+ mkdirSync2(PROJECT_APSOLUT, { recursive: true });
414
111
  }
415
112
  let projectId;
416
113
  let projectName;
417
- if (existsSync3(PROJECT_CONFIG)) {
114
+ if (existsSync2(PROJECT_CONFIG)) {
418
115
  const existing = JSON.parse(readFileSync2(PROJECT_CONFIG, "utf-8"));
419
116
  projectId = existing.id;
420
117
  projectName = existing.name;
@@ -437,7 +134,7 @@ apsolut-cortex init
437
134
  const mcpCommand = IS_DIST ? "node" : "bun";
438
135
  const mcpArgs = [mcpServerPath];
439
136
  let mcp = {};
440
- if (existsSync3(MCP_JSON)) {
137
+ if (existsSync2(MCP_JSON)) {
441
138
  try {
442
139
  mcp = JSON.parse(readFileSync2(MCP_JSON, "utf-8"));
443
140
  } catch {}
@@ -453,16 +150,16 @@ apsolut-cortex init
453
150
  console.log(`✓ Written .mcp.json`);
454
151
  const hookCmd = IS_DIST ? "apsolut-cortex" : `bun run ${join2(__dirname2, "cli.ts")}`;
455
152
  const hookEntries = {
456
- SessionStart: [{ command: `${hookCmd} hook:session-start` }],
457
- PostToolUse: [{ command: `${hookCmd} hook:post-tool-use` }],
458
- Stop: [{ command: `${hookCmd} hook:stop` }],
459
- 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` }] }]
460
157
  };
461
158
  let settings = {};
462
159
  const settingsDir = dirname2(CLAUDE_SETTINGS);
463
- if (!existsSync3(settingsDir))
464
- mkdirSync3(settingsDir, { recursive: true });
465
- if (existsSync3(CLAUDE_SETTINGS)) {
160
+ if (!existsSync2(settingsDir))
161
+ mkdirSync2(settingsDir, { recursive: true });
162
+ if (existsSync2(CLAUDE_SETTINGS)) {
466
163
  try {
467
164
  settings = JSON.parse(readFileSync2(CLAUDE_SETTINGS, "utf-8"));
468
165
  } catch {}
@@ -481,7 +178,7 @@ apsolut-cortex init
481
178
  writeFileSync2(CLAUDE_SETTINGS, JSON.stringify(settings, null, 2));
482
179
  console.log(added > 0 ? `✓ Registered ${added} hooks in ~/.claude/settings.json` : `✓ Hooks already registered`);
483
180
  const gitignore = join2(PROJECT_ROOT, ".gitignore");
484
- if (existsSync3(gitignore)) {
181
+ if (existsSync2(gitignore)) {
485
182
  const content = readFileSync2(gitignore, "utf-8");
486
183
  if (!content.includes(".apsolut/")) {
487
184
  writeFileSync2(gitignore, content + `
@@ -501,7 +198,7 @@ apsolut-cortex init
501
198
  │ ██║ ██║██║ ███████║╚██████╔╝███████╗╚██████╔╝ ██║ │
502
199
  │ ╚═╝ ╚═╝╚═╝ ╚══════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ │
503
200
  │ │
504
- │ c o r t e x · v 0 . 1 . 0
201
+ │ c o r t e x · v ${PKG_VERSION}
505
202
  │ │
506
203
  └─────────────────────────────────────────────────────────┘
507
204
 
@@ -519,13 +216,13 @@ apsolut-cortex init
519
216
  console.log(BANNER);
520
217
  }
521
218
  async function status() {
522
- const { getDb: getDb2 } = await Promise.resolve().then(() => (init_db(), exports_db));
523
- if (!existsSync3(PROJECT_CONFIG)) {
219
+ const { getDb } = await import(pathToFileURL(join2(__dirname2, "db.js")).href);
220
+ if (!existsSync2(PROJECT_CONFIG)) {
524
221
  console.log("No project found. Run: apsolut-cortex init");
525
222
  process.exit(1);
526
223
  }
527
224
  const project = JSON.parse(readFileSync2(PROJECT_CONFIG, "utf-8"));
528
- const db = getDb2();
225
+ const db = getDb();
529
226
  const total = db.prepare("SELECT COUNT(*) as n FROM memories WHERE project_id = ?").get(project.id).n;
530
227
  const sessions = db.prepare("SELECT COUNT(*) as n FROM sessions WHERE project_id = ?").get(project.id).n;
531
228
  const byTrust = db.prepare(`
@@ -555,7 +252,7 @@ DB: ~/.apsolut/memory.db
555
252
  `);
556
253
  }
557
254
  function uninstall() {
558
- if (existsSync3(MCP_JSON)) {
255
+ if (existsSync2(MCP_JSON)) {
559
256
  try {
560
257
  const mcp = JSON.parse(readFileSync2(MCP_JSON, "utf-8"));
561
258
  if (mcp.mcpServers?.["apsolut-cortex"]) {
@@ -565,14 +262,21 @@ function uninstall() {
565
262
  }
566
263
  } catch {}
567
264
  }
568
- if (existsSync3(CLAUDE_SETTINGS)) {
265
+ if (existsSync2(CLAUDE_SETTINGS)) {
569
266
  try {
570
267
  const settings = JSON.parse(readFileSync2(CLAUDE_SETTINGS, "utf-8"));
571
268
  const hooks = settings.hooks;
572
269
  if (hooks) {
573
270
  for (const event of ["SessionStart", "PostToolUse", "Stop", "SessionEnd"]) {
574
271
  if (hooks[event]) {
575
- 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
+ });
576
280
  }
577
281
  }
578
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.2",
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": {