claude-cortex 1.8.3 → 1.9.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 CHANGED
@@ -77,7 +77,6 @@ Add to `~/.claude/settings.json` for automatic memory extraction and context loa
77
77
  "hooks": {
78
78
  "PreCompact": [
79
79
  {
80
- "matcher": "",
81
80
  "hooks": [
82
81
  {
83
82
  "type": "command",
@@ -89,7 +88,6 @@ Add to `~/.claude/settings.json` for automatic memory extraction and context loa
89
88
  ],
90
89
  "SessionStart": [
91
90
  {
92
- "matcher": "",
93
91
  "hooks": [
94
92
  {
95
93
  "type": "command",
@@ -98,6 +96,17 @@ Add to `~/.claude/settings.json` for automatic memory extraction and context loa
98
96
  }
99
97
  ]
100
98
  }
99
+ ],
100
+ "SessionEnd": [
101
+ {
102
+ "hooks": [
103
+ {
104
+ "type": "command",
105
+ "command": "npx -y claude-cortex hook session-end",
106
+ "timeout": 10
107
+ }
108
+ ]
109
+ }
101
110
  ]
102
111
  }
103
112
  }
@@ -105,6 +114,7 @@ Add to `~/.claude/settings.json` for automatic memory extraction and context loa
105
114
 
106
115
  - **PreCompact**: Auto-saves important context before compaction events
107
116
  - **SessionStart**: Auto-loads project context at the start of each session
117
+ - **SessionEnd**: Auto-saves context when the session exits
108
118
 
109
119
  ### 4. Run Setup (Recommended)
110
120
 
@@ -246,6 +256,22 @@ Claude: Let me check my memory.
246
256
  > Found: "Using PostgreSQL for the database" (architecture, 95% salience)
247
257
  ```
248
258
 
259
+ ## Hook Coverage
260
+
261
+ Claude Cortex uses three hooks to cover the full session lifecycle:
262
+
263
+ | Hook | Fires When | What It Does | Reliability |
264
+ |------|-----------|--------------|-------------|
265
+ | **SessionStart** | Session begins | Loads project context from memory | Reliable |
266
+ | **PreCompact** | Before context compaction | Extracts important content before context is lost | Reliable (primary safety net) |
267
+ | **SessionEnd** | Session terminates | Extracts important content on exit | Best-effort* |
268
+
269
+ *SessionEnd does not fire on forced termination (terminal killed, SSH drops, crash). PreCompact remains the primary safety net since compaction happens more frequently than session exits.
270
+
271
+ ### Stop Hook (Opt-in, Future)
272
+
273
+ A prompt-based Stop hook that uses Haiku to evaluate each Claude response for important events is planned. This calls the Haiku API on every response, which adds latency and cost. It will be opt-in via `--with-stop-hook` flag.
274
+
249
275
  ## Configuration
250
276
 
251
277
  ### Database Location
@@ -317,6 +343,8 @@ npx claude-cortex service status # Check service status
317
343
  npx claude-cortex clawdbot install # Install Clawdbot/Moltbot hook manually
318
344
  npx claude-cortex clawdbot uninstall # Remove Clawdbot/Moltbot hook
319
345
  npx claude-cortex clawdbot status # Check Clawdbot hook status
346
+ npx claude-cortex doctor # Check installation health
347
+ npx claude-cortex --version # Show version
320
348
  ```
321
349
 
322
350
  Works on **macOS** (launchd), **Linux** (systemd), and **Windows** (Startup folder). The dashboard and API server will start automatically on login.
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@
15
15
  * npx claude-cortex setup # Configure Claude for proactive memory use
16
16
  * npx claude-cortex hook pre-compact # Run pre-compact hook (for settings.json)
17
17
  * npx claude-cortex hook session-start # Run session-start hook (for settings.json)
18
+ * npx claude-cortex hook session-end # Run session-end hook (for settings.json)
18
19
  * npx claude-cortex service install # Auto-start dashboard on login
19
20
  * npx claude-cortex service uninstall # Remove auto-start
20
21
  * npx claude-cortex service status # Check service status
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG"}
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@
15
15
  * npx claude-cortex setup # Configure Claude for proactive memory use
16
16
  * npx claude-cortex hook pre-compact # Run pre-compact hook (for settings.json)
17
17
  * npx claude-cortex hook session-start # Run session-start hook (for settings.json)
18
+ * npx claude-cortex hook session-end # Run session-end hook (for settings.json)
18
19
  * npx claude-cortex service install # Auto-start dashboard on login
19
20
  * npx claude-cortex service uninstall # Remove auto-start
20
21
  * npx claude-cortex service status # Check service status
@@ -32,6 +33,9 @@ import { handleServiceCommand } from './service/install.js';
32
33
  import { setupClaudeMd } from './setup/claude-md.js';
33
34
  import { handleHookCommand } from './setup/hooks.js';
34
35
  import { handleClawdbotCommand } from './setup/clawdbot.js';
36
+ import { createRequire } from 'module';
37
+ const require = createRequire(import.meta.url);
38
+ const pkg = require('../package.json');
35
39
  // Get the directory of this file for relative paths
36
40
  const __filename = fileURLToPath(import.meta.url);
37
41
  const __dirname = path.dirname(__filename);
@@ -129,6 +133,17 @@ function startDashboard() {
129
133
  * Main entry point
130
134
  */
131
135
  async function main() {
136
+ // Handle --version / -v
137
+ if (process.argv[2] === '--version' || process.argv[2] === '-v') {
138
+ console.log(pkg.version);
139
+ return;
140
+ }
141
+ // Handle "doctor" subcommand
142
+ if (process.argv[2] === 'doctor') {
143
+ const { handleDoctorCommand } = await import('./setup/doctor.js');
144
+ await handleDoctorCommand();
145
+ return;
146
+ }
132
147
  // Handle "setup" subcommand
133
148
  if (process.argv[2] === 'setup') {
134
149
  await setupClaudeMd();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,KAAK,EAAgB,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAEvC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AASvC,oDAAoD;AACpD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,+BAA+B;AAC/B,SAAS,SAAS;IAChB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,MAAM,GAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAErC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACtC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5B,CAAC,EAAE,CAAC;QACN,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,aAAa,EAAE,CAAC;YACrC,MAAM,CAAC,IAAI,GAAG,WAAW,CAAC;QAC5B,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YACvC,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;gBAChF,MAAM,CAAC,IAAI,GAAG,IAAkB,CAAC;YACnC,CAAC;YACD,CAAC,EAAE,CAAC;QACN,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,cAAc,CAAC,MAAe;IAC3C,wBAAwB;IACxB,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAEpC,8BAA8B;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,2BAA2B;IAC3B,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QAC/B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,cAAc;IACrB,uEAAuE;IACvE,2DAA2D;IAC3D,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IAEhE,OAAO,CAAC,GAAG,CAAC;;;;;;;;GAQX,CAAC,CAAC;IAEH,gEAAgE;IAChE,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE;QAC/C,GAAG,EAAE,YAAY;QACjB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;QACjC,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IAEH,oCAAoC;IACpC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;QACrC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,OAAO,CAAC,KAAK,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;QACvC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC9B,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7D,OAAO,CAAC,KAAK,CAAC,gFAAgF,CAAC,CAAC;IAClG,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;QACpC,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CAAC,gCAAgC,MAAM,EAAE,CAAC,CAAC;QACxD,CAAC;aAAM,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,gCAAgC,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,IAAI;IACjB,wBAAwB;IACxB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAChE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACzB,OAAO;IACT,CAAC;IAED,6BAA6B;IAC7B,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;QAClE,MAAM,mBAAmB,EAAE,CAAC;QAC5B,OAAO;IACT,CAAC;IAED,4BAA4B;IAC5B,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC;QAChC,MAAM,aAAa,EAAE,CAAC;QACtB,OAAO;IACT,CAAC;IAED,2BAA2B;IAC3B,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;QAC/B,MAAM,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC/C,OAAO;IACT,CAAC;IAED,+BAA+B;IAC/B,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACnD,OAAO;IACT,CAAC;IAED,yDAAyD;IACzD,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,oBAAoB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAClD,OAAO;IACT,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,SAAS,EAAE,CAAC;IAErC,IAAI,gBAAgB,GAAwB,IAAI,CAAC;IAEjD,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,8CAA8C;QAC9C,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;QACrD,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;SAAM,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,2CAA2C;QAC3C,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,wBAAwB,CAAC,MAAM,CAAC,CAAC;QACjC,gBAAgB,GAAG,cAAc,EAAE,CAAC;QAEpC,uCAAuC;QACvC,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAE,EAAE;YAClC,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,oBAAoB,CAAC,CAAC;YACtD,IAAI,gBAAgB,EAAE,CAAC;gBACrB,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACnC,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC;QAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IACnD,CAAC;SAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,oDAAoD;QACpD,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;QACvD,wBAAwB,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;SAAM,CAAC;QACN,mDAAmD;QACnD,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,MAAM;AACN,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,iDAAiD;IACjD,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * `npx claude-cortex doctor` — diagnostic checks for Cortex installation health.
3
+ */
4
+ export declare function handleDoctorCommand(): Promise<void>;
5
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/setup/doctor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAuIH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CA2BzD"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * `npx claude-cortex doctor` — diagnostic checks for Cortex installation health.
3
+ */
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import os from 'os';
7
+ import { createRequire } from 'module';
8
+ import { fileURLToPath } from 'url';
9
+ const require = createRequire(import.meta.url);
10
+ const results = [];
11
+ function add(status, message) {
12
+ results.push({ status, message });
13
+ }
14
+ function getDbPath() {
15
+ const newPath = path.join(os.homedir(), '.claude-cortex', 'memories.db');
16
+ const legacyPath = path.join(os.homedir(), '.claude-memory', 'memories.db');
17
+ if (fs.existsSync(newPath))
18
+ return { path: newPath, isLegacy: false };
19
+ if (fs.existsSync(legacyPath))
20
+ return { path: legacyPath, isLegacy: true };
21
+ return null;
22
+ }
23
+ function formatBytes(bytes) {
24
+ if (bytes < 1024)
25
+ return `${bytes} B`;
26
+ if (bytes < 1024 * 1024)
27
+ return `${(bytes / 1024).toFixed(1)} KB`;
28
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
29
+ }
30
+ function checkNode() {
31
+ const major = parseInt(process.version.slice(1), 10);
32
+ if (major >= 18) {
33
+ add('PASS', `Node.js ${process.version} (>= 18 required)`);
34
+ }
35
+ else {
36
+ add('WARN', `Node.js ${process.version} — version 18+ recommended`);
37
+ }
38
+ }
39
+ function checkDatabase() {
40
+ const db = getDbPath();
41
+ if (!db) {
42
+ add('FAIL', 'Database not found at ~/.claude-cortex/memories.db');
43
+ return;
44
+ }
45
+ const label = db.isLegacy ? '~/.claude-memory/memories.db (legacy)' : '~/.claude-cortex/memories.db';
46
+ try {
47
+ const stat = fs.statSync(db.path);
48
+ const Database = require('better-sqlite3');
49
+ const conn = new Database(db.path, { readonly: true });
50
+ const row = conn.prepare('SELECT COUNT(*) as count FROM memories').get();
51
+ conn.close();
52
+ add('PASS', `Database: ${label} (${row.count} memories, ${formatBytes(stat.size)})`);
53
+ }
54
+ catch (err) {
55
+ add('FAIL', `Database: ${label} — ${err.message}`);
56
+ }
57
+ }
58
+ function checkClaudeMd() {
59
+ const claudeMdPath = path.join(os.homedir(), '.claude', 'CLAUDE.md');
60
+ if (!fs.existsSync(claudeMdPath)) {
61
+ add('WARN', 'CLAUDE.md not found — run `npx claude-cortex setup`');
62
+ return;
63
+ }
64
+ const content = fs.readFileSync(claudeMdPath, 'utf-8');
65
+ if (content.includes('# Claude Cortex')) {
66
+ add('PASS', 'CLAUDE.md: Cortex instructions present');
67
+ }
68
+ else {
69
+ add('WARN', 'CLAUDE.md: Cortex instructions not found — run `npx claude-cortex setup`');
70
+ }
71
+ }
72
+ function checkHooks() {
73
+ const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
74
+ if (!fs.existsSync(settingsPath)) {
75
+ add('WARN', 'settings.json not found — hooks not configured');
76
+ return;
77
+ }
78
+ let settings;
79
+ try {
80
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
81
+ }
82
+ catch {
83
+ add('FAIL', 'settings.json: failed to parse');
84
+ return;
85
+ }
86
+ const hooks = settings?.hooks || {};
87
+ const expected = ['PreCompact', 'SessionStart', 'SessionEnd'];
88
+ for (const name of expected) {
89
+ const entries = hooks[name];
90
+ const hasCortex = Array.isArray(entries) && entries.some((e) => Array.isArray(e?.hooks) && e.hooks.some((h) => typeof h?.command === 'string' && h.command.includes('claude-cortex')));
91
+ if (hasCortex) {
92
+ add('PASS', `Hook: ${name} configured`);
93
+ }
94
+ else {
95
+ add('WARN', `Hook: ${name} not configured`);
96
+ }
97
+ }
98
+ }
99
+ function checkMcp() {
100
+ // Check project-level .mcp.json
101
+ const projectMcp = path.join(process.cwd(), '.mcp.json');
102
+ // Check user-level config
103
+ const userMcp = path.join(os.homedir(), '.claude.json');
104
+ for (const p of [projectMcp, userMcp]) {
105
+ if (fs.existsSync(p)) {
106
+ try {
107
+ const content = fs.readFileSync(p, 'utf-8');
108
+ if (content.includes('claude-cortex')) {
109
+ add('PASS', `MCP: cortex configured in ${path.basename(p)}`);
110
+ return;
111
+ }
112
+ }
113
+ catch { /* ignore parse errors */ }
114
+ }
115
+ }
116
+ add('WARN', 'MCP: no cortex entry found in .mcp.json or ~/.claude.json');
117
+ }
118
+ export async function handleDoctorCommand() {
119
+ const __filename = fileURLToPath(import.meta.url);
120
+ const __dirname = path.dirname(__filename);
121
+ const pkg = require(path.resolve(__dirname, '..', '..', 'package.json'));
122
+ console.log(`\nClaude Cortex Doctor v${pkg.version}\n`);
123
+ checkNode();
124
+ checkDatabase();
125
+ checkClaudeMd();
126
+ checkHooks();
127
+ checkMcp();
128
+ // Print results
129
+ for (const r of results) {
130
+ const tag = r.status === 'PASS' ? '\x1b[32m PASS\x1b[0m'
131
+ : r.status === 'WARN' ? '\x1b[33m WARN\x1b[0m'
132
+ : '\x1b[31m FAIL\x1b[0m';
133
+ console.log(`${tag} ${r.message}`);
134
+ }
135
+ const passed = results.filter(r => r.status === 'PASS').length;
136
+ const warns = results.filter(r => r.status === 'WARN').length;
137
+ const fails = results.filter(r => r.status === 'FAIL').length;
138
+ console.log(`\n${passed} passed, ${warns} warnings, ${fails} failed\n`);
139
+ process.exit(fails > 0 ? 1 : 0);
140
+ }
141
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/setup/doctor.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/setup/hooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAiBH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBvE"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/setup/hooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkBH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBvE"}
@@ -12,13 +12,14 @@ const SCRIPTS_DIR = path.resolve(__dirname, '..', '..', 'scripts');
12
12
  const HOOKS = {
13
13
  'pre-compact': 'pre-compact-hook.mjs',
14
14
  'session-start': 'session-start-hook.mjs',
15
+ 'session-end': 'session-end-hook.mjs',
15
16
  };
16
17
  export async function handleHookCommand(hookName) {
17
18
  const scriptFile = HOOKS[hookName];
18
19
  if (!scriptFile) {
19
20
  console.error(`Unknown hook: ${hookName}`);
20
21
  console.log(`Available hooks: ${Object.keys(HOOKS).join(', ')}`);
21
- console.log('Usage: claude-cortex hook <pre-compact|session-start>');
22
+ console.log('Usage: claude-cortex hook <pre-compact|session-start|session-end>');
22
23
  process.exit(1);
23
24
  }
24
25
  const scriptPath = path.join(SCRIPTS_DIR, scriptFile);
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/setup/hooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,qDAAqD;AACrD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;AAEnE,MAAM,KAAK,GAA2B;IACpC,aAAa,EAAE,sBAAsB;IACrC,eAAe,EAAE,wBAAwB;CAC1C,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAgB;IACtD,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,iBAAiB,QAAQ,EAAE,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjE,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IAEtD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,EAAE;QAClD,KAAK,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC;KACtC,CAAC,CAAC;IAEH,kCAAkC;IAClC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEhC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACxB,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/setup/hooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,qDAAqD;AACrD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;AAEnE,MAAM,KAAK,GAA2B;IACpC,aAAa,EAAE,sBAAsB;IACrC,eAAe,EAAE,wBAAwB;IACzC,aAAa,EAAE,sBAAsB;CACtC,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAgB;IACtD,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,iBAAiB,QAAQ,EAAE,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjE,OAAO,CAAC,GAAG,CAAC,mEAAmE,CAAC,CAAC;QACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IAEtD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,EAAE;QAClD,KAAK,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC;KACtC,CAAC,CAAC;IAEH,kCAAkC;IAClC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEhC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACxB,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-cortex",
3
- "version": "1.8.3",
3
+ "version": "1.9.1",
4
4
  "description": "Brain-like memory system for Claude Code - solves context compaction and memory persistence",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -0,0 +1,548 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Session-end hook for Claude Cortex - Automatic Memory Extraction on Exit
4
+ *
5
+ * This script runs when a Claude Code session ends and:
6
+ * 1. Reads the session transcript from the JSONL file
7
+ * 2. Analyzes conversation content for important information
8
+ * 3. Auto-extracts high-salience items (decisions, patterns, errors, etc.)
9
+ * 4. Saves them to the memory database automatically
10
+ *
11
+ * NOTE: SessionEnd doesn't always fire reliably (e.g. terminal killed, SSH drops).
12
+ * PreCompact remains the primary safety net for context preservation.
13
+ *
14
+ * Input (stdin JSON):
15
+ * {
16
+ * "session_id": "abc123",
17
+ * "transcript_path": "~/.claude/projects/.../abc.jsonl",
18
+ * "cwd": "/path/to/project",
19
+ * "hook_event_name": "SessionEnd",
20
+ * "reason": "exit" | "clear" | "logout" | "prompt_input_exit" | "other"
21
+ * }
22
+ */
23
+
24
+ import Database from 'better-sqlite3';
25
+ import { existsSync, mkdirSync, readFileSync } from 'fs';
26
+ import { join } from 'path';
27
+ import { homedir } from 'os';
28
+
29
+ // Database paths (with legacy fallback)
30
+ const NEW_DB_DIR = join(homedir(), '.claude-cortex');
31
+ const LEGACY_DB_DIR = join(homedir(), '.claude-memory');
32
+
33
+ function getDbPath() {
34
+ const newPath = join(NEW_DB_DIR, 'memories.db');
35
+ const legacyPath = join(LEGACY_DB_DIR, 'memories.db');
36
+ if (existsSync(newPath) || !existsSync(legacyPath)) {
37
+ return { dir: NEW_DB_DIR, path: newPath };
38
+ }
39
+ return { dir: LEGACY_DB_DIR, path: legacyPath };
40
+ }
41
+
42
+ const { dir: DB_DIR, path: DB_PATH } = getDbPath();
43
+
44
+ // Memory limits
45
+ const MAX_SHORT_TERM_MEMORIES = 100;
46
+ const MAX_LONG_TERM_MEMORIES = 1000;
47
+ const BASE_THRESHOLD = 0.35;
48
+ const MAX_AUTO_MEMORIES = 5;
49
+
50
+ const CATEGORY_EXTRACTION_THRESHOLDS = {
51
+ architecture: 0.28,
52
+ error: 0.30,
53
+ context: 0.32,
54
+ learning: 0.32,
55
+ pattern: 0.35,
56
+ preference: 0.38,
57
+ note: 0.42,
58
+ todo: 0.40,
59
+ relationship: 0.35,
60
+ custom: 0.35,
61
+ };
62
+
63
+ // ==================== PROJECT DETECTION ====================
64
+
65
+ const SKIP_DIRECTORIES = [
66
+ 'src', 'lib', 'dist', 'build', 'out',
67
+ 'node_modules', '.git', '.next', '.cache',
68
+ 'test', 'tests', '__tests__', 'spec',
69
+ 'bin', 'scripts', 'config', 'public', 'static',
70
+ ];
71
+
72
+ function extractProjectFromPath(path) {
73
+ if (!path) return null;
74
+ const segments = path.split(/[/\\]/).filter(Boolean);
75
+ if (segments.length === 0) return null;
76
+ for (let i = segments.length - 1; i >= 0; i--) {
77
+ const segment = segments[i];
78
+ if (!SKIP_DIRECTORIES.includes(segment.toLowerCase())) {
79
+ if (segment.startsWith('.')) continue;
80
+ return segment;
81
+ }
82
+ }
83
+ return null;
84
+ }
85
+
86
+ // ==================== DYNAMIC THRESHOLD ====================
87
+
88
+ function getMemoryStats(db) {
89
+ try {
90
+ const stats = db.prepare(`
91
+ SELECT
92
+ COUNT(*) as total,
93
+ SUM(CASE WHEN type = 'short_term' THEN 1 ELSE 0 END) as shortTerm,
94
+ SUM(CASE WHEN type = 'long_term' THEN 1 ELSE 0 END) as longTerm
95
+ FROM memories
96
+ `).get();
97
+ return stats || { total: 0, shortTerm: 0, longTerm: 0 };
98
+ } catch {
99
+ return { total: 0, shortTerm: 0, longTerm: 0 };
100
+ }
101
+ }
102
+
103
+ function getDynamicThreshold(memoryCount, maxMemories) {
104
+ const fullness = memoryCount / maxMemories;
105
+ if (fullness > 0.8) return 0.50;
106
+ if (fullness > 0.6) return 0.42;
107
+ if (fullness > 0.4) return 0.35;
108
+ if (fullness > 0.2) return 0.30;
109
+ return 0.25;
110
+ }
111
+
112
+ function getExtractionThreshold(category, dynamicThreshold) {
113
+ const categoryThreshold = CATEGORY_EXTRACTION_THRESHOLDS[category] || BASE_THRESHOLD;
114
+ return Math.min(categoryThreshold, dynamicThreshold);
115
+ }
116
+
117
+ // ==================== SALIENCE DETECTION ====================
118
+
119
+ const ARCHITECTURE_KEYWORDS = [
120
+ 'architecture', 'design', 'pattern', 'structure', 'system',
121
+ 'database', 'api', 'schema', 'model', 'framework', 'stack',
122
+ 'microservice', 'monolith', 'serverless', 'infrastructure'
123
+ ];
124
+
125
+ const ERROR_KEYWORDS = [
126
+ 'error', 'bug', 'fix', 'issue', 'problem', 'crash', 'fail',
127
+ 'exception', 'debug', 'resolve', 'solution', 'workaround'
128
+ ];
129
+
130
+ const PREFERENCE_KEYWORDS = [
131
+ 'prefer', 'always', 'never', 'style', 'convention', 'standard',
132
+ 'like', 'want', 'should', 'must', 'require'
133
+ ];
134
+
135
+ const PATTERN_KEYWORDS = [
136
+ 'pattern', 'practice', 'approach', 'method', 'technique',
137
+ 'implementation', 'strategy', 'algorithm', 'workflow'
138
+ ];
139
+
140
+ const DECISION_KEYWORDS = [
141
+ 'decided', 'decision', 'chose', 'chosen', 'selected', 'going with',
142
+ 'will use', 'opted for', 'settled on', 'agreed'
143
+ ];
144
+
145
+ const LEARNING_KEYWORDS = [
146
+ 'learned', 'discovered', 'realized', 'found out', 'turns out',
147
+ 'TIL', 'now know', 'understand now', 'figured out'
148
+ ];
149
+
150
+ const EMOTIONAL_MARKERS = [
151
+ 'important', 'critical', 'crucial', 'essential', 'key',
152
+ 'finally', 'breakthrough', 'eureka', 'aha', 'got it',
153
+ 'frustrating', 'annoying', 'tricky', 'remember'
154
+ ];
155
+
156
+ const CODE_REFERENCE_PATTERNS = [
157
+ /\b[A-Z][a-zA-Z]*\.[a-zA-Z]+\b/,
158
+ /\b[a-z_][a-zA-Z0-9_]*\.(ts|js|py|go|rs)\b/,
159
+ /`[^`]+`/,
160
+ /\b(function|class|interface|type|const|let|var)\s+\w+/i,
161
+ /\bline\s*\d+\b/i,
162
+ /\b(src|lib|app|components?)\/\S+/,
163
+ ];
164
+
165
+ function detectKeywords(text, keywords) {
166
+ const lower = text.toLowerCase();
167
+ return keywords.some(keyword => lower.includes(keyword.toLowerCase()));
168
+ }
169
+
170
+ function detectCodeReferences(content) {
171
+ return CODE_REFERENCE_PATTERNS.some(pattern => pattern.test(content));
172
+ }
173
+
174
+ function detectExplicitRequest(text) {
175
+ const patterns = [
176
+ /\bremember\s+(this|that)\b/i,
177
+ /\bdon'?t\s+forget\b/i,
178
+ /\bkeep\s+(in\s+)?mind\b/i,
179
+ /\bnote\s+(this|that)\b/i,
180
+ /\bsave\s+(this|that)\b/i,
181
+ /\bimportant[:\s]/i,
182
+ /\bfor\s+future\s+reference\b/i,
183
+ ];
184
+ return patterns.some(pattern => pattern.test(text));
185
+ }
186
+
187
+ function calculateSalience(text) {
188
+ let score = 0.25;
189
+ if (detectExplicitRequest(text)) score += 0.5;
190
+ if (detectKeywords(text, ARCHITECTURE_KEYWORDS)) score += 0.4;
191
+ if (detectKeywords(text, ERROR_KEYWORDS)) score += 0.35;
192
+ if (detectKeywords(text, DECISION_KEYWORDS)) score += 0.35;
193
+ if (detectKeywords(text, LEARNING_KEYWORDS)) score += 0.3;
194
+ if (detectKeywords(text, PATTERN_KEYWORDS)) score += 0.25;
195
+ if (detectKeywords(text, PREFERENCE_KEYWORDS)) score += 0.25;
196
+ if (detectCodeReferences(text)) score += 0.15;
197
+ if (detectKeywords(text, EMOTIONAL_MARKERS)) score += 0.2;
198
+ return Math.min(1.0, score);
199
+ }
200
+
201
+ function suggestCategory(text) {
202
+ const lower = text.toLowerCase();
203
+ if (detectKeywords(lower, ARCHITECTURE_KEYWORDS)) return 'architecture';
204
+ if (detectKeywords(lower, ERROR_KEYWORDS)) return 'error';
205
+ if (detectKeywords(lower, DECISION_KEYWORDS)) return 'context';
206
+ if (detectKeywords(lower, LEARNING_KEYWORDS)) return 'learning';
207
+ if (detectKeywords(lower, PREFERENCE_KEYWORDS)) return 'preference';
208
+ if (detectKeywords(lower, PATTERN_KEYWORDS)) return 'pattern';
209
+ if (/\b(todo|fixme|hack|xxx)\b/i.test(lower)) return 'todo';
210
+ return 'note';
211
+ }
212
+
213
+ function extractTags(text, extractorName = null) {
214
+ const tags = new Set();
215
+ const hashtagMatches = text.match(/#[a-zA-Z][a-zA-Z0-9_-]*/g);
216
+ if (hashtagMatches) {
217
+ hashtagMatches.forEach(tag => tags.add(tag.slice(1).toLowerCase()));
218
+ }
219
+ const techTerms = [
220
+ 'react', 'vue', 'angular', 'node', 'python', 'typescript', 'javascript',
221
+ 'api', 'database', 'sql', 'mongodb', 'postgresql', 'mysql',
222
+ 'docker', 'kubernetes', 'aws', 'git', 'testing', 'auth', 'security'
223
+ ];
224
+ const lowerText = text.toLowerCase();
225
+ techTerms.forEach(term => {
226
+ if (lowerText.includes(term)) tags.add(term);
227
+ });
228
+ tags.add('auto-extracted');
229
+ tags.add('session-end');
230
+ if (extractorName) {
231
+ tags.add(`source:${extractorName}`);
232
+ }
233
+ return Array.from(tags).slice(0, 12);
234
+ }
235
+
236
+ function calculateFrequencyBoost(segment, allSegments) {
237
+ const commonWords = new Set([
238
+ 'about', 'after', 'before', 'being', 'between', 'could', 'during',
239
+ 'every', 'found', 'through', 'would', 'should', 'which', 'where',
240
+ 'there', 'these', 'their', 'other', 'using', 'because', 'without'
241
+ ]);
242
+ const words = segment.content.toLowerCase().split(/\s+/);
243
+ const keyTerms = words.filter(w =>
244
+ w.length > 5 && !commonWords.has(w) && /^[a-z]+$/.test(w)
245
+ );
246
+ let boost = 0;
247
+ const seenTerms = new Set();
248
+ for (const term of keyTerms) {
249
+ if (seenTerms.has(term)) continue;
250
+ seenTerms.add(term);
251
+ const mentions = allSegments.filter(s =>
252
+ s !== segment && s.content.toLowerCase().includes(term)
253
+ ).length;
254
+ if (mentions > 1) {
255
+ boost += 0.03 * Math.min(mentions, 5);
256
+ }
257
+ }
258
+ return Math.min(0.15, boost);
259
+ }
260
+
261
+ // ==================== CONTENT EXTRACTION ====================
262
+
263
+ function extractMemorableSegments(conversationText) {
264
+ const segments = [];
265
+ const extractors = [
266
+ {
267
+ name: 'decision',
268
+ patterns: [
269
+ /(?:we\s+)?decided\s+(?:to\s+)?(.{15,200})/gi,
270
+ /(?:going|went)\s+with\s+(.{15,150})/gi,
271
+ /(?:chose|chosen|selected)\s+(.{15,150})/gi,
272
+ /the\s+(?:approach|solution|fix)\s+(?:is|was)\s+(.{15,200})/gi,
273
+ /(?:using|will\s+use)\s+(.{15,150})/gi,
274
+ /(?:opted\s+for|settled\s+on)\s+(.{15,150})/gi,
275
+ ],
276
+ titlePrefix: 'Decision: ',
277
+ },
278
+ {
279
+ name: 'error-fix',
280
+ patterns: [
281
+ /(?:fixed|solved|resolved)\s+(?:by\s+)?(.{15,200})/gi,
282
+ /the\s+(?:fix|solution|workaround)\s+(?:is|was)\s+(.{15,200})/gi,
283
+ /(?:root\s+cause|issue)\s+(?:is|was)\s+(.{15,200})/gi,
284
+ /(?:error|bug)\s+(?:was\s+)?caused\s+by\s+(.{15,200})/gi,
285
+ /(?:problem|issue)\s+was\s+(.{15,150})/gi,
286
+ /(?:the\s+)?bug\s+(?:is|was)\s+(.{15,150})/gi,
287
+ /(?:debugging|debugged)\s+(.{15,150})/gi,
288
+ ],
289
+ titlePrefix: 'Fix: ',
290
+ },
291
+ {
292
+ name: 'learning',
293
+ patterns: [
294
+ /(?:learned|discovered|realized|found\s+out)\s+(?:that\s+)?(.{15,200})/gi,
295
+ /turns\s+out\s+(?:that\s+)?(.{15,200})/gi,
296
+ /(?:TIL|today\s+I\s+learned)[:\s]+(.{15,200})/gi,
297
+ /(?:now\s+)?(?:understand|know)\s+(?:that\s+)?(.{15,150})/gi,
298
+ /(?:figured\s+out|worked\s+out)\s+(.{15,150})/gi,
299
+ ],
300
+ titlePrefix: 'Learned: ',
301
+ },
302
+ {
303
+ name: 'architecture',
304
+ patterns: [
305
+ /the\s+architecture\s+(?:is|uses|consists\s+of)\s+(.{15,200})/gi,
306
+ /(?:design|pattern)\s+(?:is|uses)\s+(.{15,200})/gi,
307
+ /(?:system|api|database)\s+(?:structure|design)\s+(?:is|uses)\s+(.{15,200})/gi,
308
+ /(?:created|added|implemented|built)\s+(?:a\s+)?(.{15,200})/gi,
309
+ /(?:refactored|updated|changed)\s+(?:the\s+)?(.{15,150})/gi,
310
+ ],
311
+ titlePrefix: 'Architecture: ',
312
+ },
313
+ {
314
+ name: 'preference',
315
+ patterns: [
316
+ /(?:always|never)\s+(.{10,150})/gi,
317
+ /(?:prefer|want)\s+to\s+(.{10,150})/gi,
318
+ /(?:should|must)\s+(?:always\s+)?(.{10,150})/gi,
319
+ ],
320
+ titlePrefix: 'Preference: ',
321
+ },
322
+ {
323
+ name: 'important-note',
324
+ patterns: [
325
+ /important[:\s]+(.{15,200})/gi,
326
+ /(?:note|remember)[:\s]+(.{15,200})/gi,
327
+ /(?:key|critical)\s+(?:point|thing)[:\s]+(.{15,200})/gi,
328
+ /(?:this\s+is\s+)?(?:crucial|essential)[:\s]+(.{15,150})/gi,
329
+ /(?:don't\s+forget|keep\s+in\s+mind)[:\s]+(.{15,150})/gi,
330
+ ],
331
+ titlePrefix: 'Note: ',
332
+ },
333
+ ];
334
+
335
+ for (const extractor of extractors) {
336
+ for (const pattern of extractor.patterns) {
337
+ let match;
338
+ while ((match = pattern.exec(conversationText)) !== null) {
339
+ const content = match[1].trim();
340
+ if (content.length >= 20) {
341
+ const titleContent = content.slice(0, 50).replace(/\s+/g, ' ').trim();
342
+ const title = extractor.titlePrefix + (titleContent.length < 50 ? titleContent : titleContent + '...');
343
+ segments.push({
344
+ title,
345
+ content: content.slice(0, 500),
346
+ extractorType: extractor.name,
347
+ });
348
+ }
349
+ }
350
+ }
351
+ }
352
+
353
+ return segments;
354
+ }
355
+
356
+ function processSegments(segments, dynamicThreshold = BASE_THRESHOLD) {
357
+ const unique = [];
358
+ for (const seg of segments) {
359
+ const isDupe = unique.some(existing => {
360
+ const overlap = calculateOverlap(existing.content, seg.content);
361
+ return overlap > 0.8;
362
+ });
363
+ if (!isDupe) {
364
+ const text = seg.title + ' ' + seg.content;
365
+ const baseSalience = calculateSalience(text);
366
+ const category = suggestCategory(text);
367
+ unique.push({
368
+ ...seg,
369
+ baseSalience,
370
+ category,
371
+ tags: extractTags(text, seg.extractorType),
372
+ });
373
+ }
374
+ }
375
+
376
+ for (const seg of unique) {
377
+ const frequencyBoost = calculateFrequencyBoost(seg, unique);
378
+ seg.salience = Math.min(1.0, seg.baseSalience + frequencyBoost);
379
+ seg.frequencyBoost = frequencyBoost;
380
+ }
381
+
382
+ unique.sort((a, b) => b.salience - a.salience);
383
+
384
+ const filtered = unique.filter(seg => {
385
+ const threshold = getExtractionThreshold(seg.category, dynamicThreshold);
386
+ return seg.salience >= threshold;
387
+ });
388
+
389
+ return filtered.slice(0, MAX_AUTO_MEMORIES);
390
+ }
391
+
392
+ function calculateOverlap(text1, text2) {
393
+ const words1 = new Set(text1.toLowerCase().split(/\s+/));
394
+ const words2 = new Set(text2.toLowerCase().split(/\s+/));
395
+ const intersection = new Set([...words1].filter(w => words2.has(w)));
396
+ const union = new Set([...words1, ...words2]);
397
+ return intersection.size / union.size;
398
+ }
399
+
400
+ // ==================== DATABASE OPERATIONS ====================
401
+
402
+ function saveMemory(db, memory, project) {
403
+ const timestamp = new Date().toISOString();
404
+ const stmt = db.prepare(`
405
+ INSERT INTO memories (title, content, type, category, salience, tags, project, created_at, last_accessed)
406
+ VALUES (?, ?, 'short_term', ?, ?, ?, ?, ?, ?)
407
+ `);
408
+ stmt.run(
409
+ memory.title,
410
+ memory.content,
411
+ memory.category,
412
+ memory.salience,
413
+ JSON.stringify(memory.tags),
414
+ project || null,
415
+ timestamp,
416
+ timestamp
417
+ );
418
+ }
419
+
420
+ // ==================== TRANSCRIPT READING ====================
421
+
422
+ /**
423
+ * Read conversation text from the session transcript JSONL file.
424
+ */
425
+ function readTranscript(transcriptPath) {
426
+ if (!transcriptPath) return '';
427
+
428
+ // Expand ~ to homedir
429
+ const resolvedPath = transcriptPath.replace(/^~/, homedir());
430
+
431
+ if (!existsSync(resolvedPath)) {
432
+ console.error(`[session-end] Transcript not found: ${resolvedPath}`);
433
+ return '';
434
+ }
435
+
436
+ try {
437
+ const content = readFileSync(resolvedPath, 'utf-8');
438
+ const lines = content.trim().split('\n');
439
+
440
+ // Read last 50 lines to get recent conversation
441
+ const recentLines = lines.slice(-50);
442
+ const messages = [];
443
+
444
+ for (const line of recentLines) {
445
+ try {
446
+ const entry = JSON.parse(line);
447
+ const role = entry.type || entry.message?.role;
448
+ const msgContent = entry.message?.content;
449
+ if ((role === 'user' || role === 'assistant') && msgContent) {
450
+ const text = Array.isArray(msgContent)
451
+ ? msgContent.filter(c => c.type === 'text').map(c => c.text).join('\n')
452
+ : msgContent;
453
+ if (text && !text.startsWith('/')) {
454
+ messages.push(text);
455
+ }
456
+ }
457
+ } catch {
458
+ // Skip invalid lines
459
+ }
460
+ }
461
+
462
+ const result = messages.join('\n\n');
463
+ console.error(`[session-end] Read ${messages.length} messages from transcript (${result.length} chars)`);
464
+ return result;
465
+ } catch (err) {
466
+ console.error(`[session-end] Failed to read transcript: ${err.message}`);
467
+ return '';
468
+ }
469
+ }
470
+
471
+ // ==================== MAIN HOOK LOGIC ====================
472
+
473
+ let input = '';
474
+ process.stdin.setEncoding('utf8');
475
+
476
+ process.stdin.on('readable', () => {
477
+ let chunk;
478
+ while ((chunk = process.stdin.read()) !== null) {
479
+ input += chunk;
480
+ }
481
+ });
482
+
483
+ process.stdin.on('end', () => {
484
+ try {
485
+ const hookData = JSON.parse(input || '{}');
486
+
487
+ const reason = hookData.reason || 'unknown';
488
+ const project = extractProjectFromPath(hookData.cwd);
489
+
490
+ // Skip extraction on /clear — session is being intentionally wiped
491
+ if (reason === 'clear') {
492
+ console.error('[session-end] Session cleared, skipping extraction');
493
+ process.exit(0);
494
+ }
495
+
496
+ // Read conversation from transcript_path (provided by Claude Code)
497
+ const conversationText = readTranscript(hookData.transcript_path);
498
+
499
+ if (!conversationText || conversationText.length < 100) {
500
+ console.error('[session-end] Not enough conversation content to extract from');
501
+ process.exit(0);
502
+ }
503
+
504
+ // Ensure database directory exists
505
+ if (!existsSync(DB_DIR)) {
506
+ mkdirSync(DB_DIR, { recursive: true });
507
+ }
508
+
509
+ if (!existsSync(DB_PATH)) {
510
+ console.error('[session-end] Memory database not found, skipping extraction');
511
+ process.exit(0);
512
+ }
513
+
514
+ const db = new Database(DB_PATH, { timeout: 5000 });
515
+
516
+ const stats = getMemoryStats(db);
517
+ const totalMemories = stats.shortTerm + stats.longTerm;
518
+ const maxMemories = MAX_SHORT_TERM_MEMORIES + MAX_LONG_TERM_MEMORIES;
519
+ const dynamicThreshold = getDynamicThreshold(totalMemories, maxMemories);
520
+
521
+ console.error(`[session-end] Memory status: ${totalMemories}/${maxMemories} (${(totalMemories/maxMemories*100).toFixed(0)}% full)`);
522
+ console.error(`[session-end] Reason: ${reason}, Dynamic threshold: ${dynamicThreshold.toFixed(2)}`);
523
+
524
+ // Extract memorable segments
525
+ const segments = extractMemorableSegments(conversationText);
526
+ const processedSegments = processSegments(segments, dynamicThreshold);
527
+
528
+ let autoExtractedCount = 0;
529
+ for (const memory of processedSegments) {
530
+ try {
531
+ saveMemory(db, memory, project);
532
+ autoExtractedCount++;
533
+ const boostInfo = memory.frequencyBoost > 0 ? ` +${memory.frequencyBoost.toFixed(2)} boost` : '';
534
+ console.error(`[session-end] Saved: ${memory.title} (salience: ${memory.salience.toFixed(2)}${boostInfo}, category: ${memory.category})`);
535
+ } catch (err) {
536
+ console.error(`[session-end] Failed to save "${memory.title}": ${err.message}`);
537
+ }
538
+ }
539
+
540
+ console.error(`[session-end] Complete: ${autoExtractedCount} memories auto-extracted on session ${reason}`);
541
+
542
+ db.close();
543
+ process.exit(0);
544
+ } catch (error) {
545
+ console.error(`[session-end] Error: ${error.message}`);
546
+ process.exit(0); // Don't block session exit on errors
547
+ }
548
+ });