claude-cortex 1.9.0 → 1.10.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/README.md +10 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/setup/claude-md.d.ts +3 -1
- package/dist/setup/claude-md.d.ts.map +1 -1
- package/dist/setup/claude-md.js +6 -3
- package/dist/setup/claude-md.js.map +1 -1
- package/dist/setup/doctor.d.ts +5 -0
- package/dist/setup/doctor.d.ts.map +1 -0
- package/dist/setup/doctor.js +141 -0
- package/dist/setup/doctor.js.map +1 -0
- package/dist/setup/hooks.d.ts.map +1 -1
- package/dist/setup/hooks.js +2 -1
- package/dist/setup/hooks.js.map +1 -1
- package/dist/setup/settings-hooks.d.ts +7 -0
- package/dist/setup/settings-hooks.d.ts.map +1 -0
- package/dist/setup/settings-hooks.js +83 -0
- package/dist/setup/settings-hooks.js.map +1 -0
- package/package.json +1 -1
- package/scripts/stop-hook.mjs +163 -0
package/README.md
CHANGED
|
@@ -124,9 +124,16 @@ npx claude-cortex setup
|
|
|
124
124
|
|
|
125
125
|
This configures everything automatically:
|
|
126
126
|
- **Claude Code**: Adds proactive memory instructions to `~/.claude/CLAUDE.md`
|
|
127
|
+
- **Hooks**: Installs PreCompact, SessionStart, and SessionEnd hooks into `~/.claude/settings.json`
|
|
127
128
|
- **Clawdbot/Moltbot**: Installs `cortex-memory` hook if Clawdbot or Moltbot is detected
|
|
128
129
|
|
|
129
|
-
Safe to run multiple times (idempotent).
|
|
130
|
+
Safe to run multiple times (idempotent).
|
|
131
|
+
|
|
132
|
+
**Optional: Stop hook** — Checks the last response for notable content and prompts Claude to `remember`:
|
|
133
|
+
```bash
|
|
134
|
+
npx claude-cortex setup --with-stop-hook
|
|
135
|
+
```
|
|
136
|
+
The Stop hook uses local pattern matching (no API cost). Loop prevention is programmatic.
|
|
130
137
|
|
|
131
138
|
### 5. Use It
|
|
132
139
|
|
|
@@ -343,6 +350,8 @@ npx claude-cortex service status # Check service status
|
|
|
343
350
|
npx claude-cortex clawdbot install # Install Clawdbot/Moltbot hook manually
|
|
344
351
|
npx claude-cortex clawdbot uninstall # Remove Clawdbot/Moltbot hook
|
|
345
352
|
npx claude-cortex clawdbot status # Check Clawdbot hook status
|
|
353
|
+
npx claude-cortex doctor # Check installation health
|
|
354
|
+
npx claude-cortex --version # Show version
|
|
346
355
|
```
|
|
347
356
|
|
|
348
357
|
Works on **macOS** (launchd), **Linux** (systemd), and **Windows** (Startup folder). The dashboard and API server will start automatically on login.
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,9 @@ import { handleServiceCommand } from './service/install.js';
|
|
|
33
33
|
import { setupClaudeMd } from './setup/claude-md.js';
|
|
34
34
|
import { handleHookCommand } from './setup/hooks.js';
|
|
35
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');
|
|
36
39
|
// Get the directory of this file for relative paths
|
|
37
40
|
const __filename = fileURLToPath(import.meta.url);
|
|
38
41
|
const __dirname = path.dirname(__filename);
|
|
@@ -130,9 +133,21 @@ function startDashboard() {
|
|
|
130
133
|
* Main entry point
|
|
131
134
|
*/
|
|
132
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
|
+
}
|
|
133
147
|
// Handle "setup" subcommand
|
|
134
148
|
if (process.argv[2] === 'setup') {
|
|
135
|
-
|
|
149
|
+
const stopHook = process.argv.includes('--with-stop-hook');
|
|
150
|
+
await setupClaudeMd({ stopHook });
|
|
136
151
|
return;
|
|
137
152
|
}
|
|
138
153
|
// Handle "hook" subcommand
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-md.d.ts","sourceRoot":"","sources":["../../src/setup/claude-md.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"claude-md.d.ts","sourceRoot":"","sources":["../../src/setup/claude-md.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA6CH,wBAAsB,aAAa,CAAC,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBnF"}
|
package/dist/setup/claude-md.js
CHANGED
|
@@ -10,6 +10,7 @@ import fs from 'fs';
|
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import os from 'os';
|
|
12
12
|
import { installClawdbotHook, findClawdbotHooksDir } from './clawdbot.js';
|
|
13
|
+
import { setupHooks } from './settings-hooks.js';
|
|
13
14
|
const MARKER = '# Claude Cortex — Memory System';
|
|
14
15
|
const INSTRUCTIONS = `
|
|
15
16
|
${MARKER}
|
|
@@ -42,11 +43,13 @@ function setupClaudeCode() {
|
|
|
42
43
|
console.log('✓ Claude Code: added memory instructions to ~/.claude/CLAUDE.md');
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
|
-
export async function setupClaudeMd() {
|
|
46
|
+
export async function setupClaudeMd(options) {
|
|
46
47
|
console.log('Setting up Claude Cortex...\n');
|
|
47
|
-
// 1. Claude Code — always
|
|
48
|
+
// 1. Claude Code CLAUDE.md — always
|
|
48
49
|
setupClaudeCode();
|
|
49
|
-
// 2.
|
|
50
|
+
// 2. Hooks in settings.json
|
|
51
|
+
setupHooks(options);
|
|
52
|
+
// 3. Clawdbot/Moltbot — if detected
|
|
50
53
|
const hooksDir = findClawdbotHooksDir();
|
|
51
54
|
if (hooksDir) {
|
|
52
55
|
const hookExists = fs.existsSync(path.join(hooksDir, 'cortex-memory'));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-md.js","sourceRoot":"","sources":["../../src/setup/claude-md.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"claude-md.js","sourceRoot":"","sources":["../../src/setup/claude-md.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,MAAM,GAAG,iCAAiC,CAAC;AAEjD,MAAM,YAAY,GAAG;EACnB,MAAM;;;;;;;;;;;;CAYP,CAAC;AAEF,SAAS,eAAe;IACtB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IACrD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAEvD,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClB,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,oEAAoE,CAAC,CAAC;IACpF,CAAC;SAAM,CAAC;QACN,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,IAAI,GAAG,YAAY,CAAC;QAC5D,EAAE,CAAC,aAAa,CAAC,YAAY,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,iEAAiE,CAAC,CAAC;IACjF,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAgC;IAClE,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;IAE7C,oCAAoC;IACpC,eAAe,EAAE,CAAC;IAElB,4BAA4B;IAC5B,UAAU,CAAC,OAAO,CAAC,CAAC;IAEpB,oCAAoC;IACpC,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;IACxC,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,UAAU,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;QACvE,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;QAClE,CAAC;aAAM,CAAC;YACN,MAAM,mBAAmB,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/setup/hooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAmBH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBvE"}
|
package/dist/setup/hooks.js
CHANGED
|
@@ -13,13 +13,14 @@ const HOOKS = {
|
|
|
13
13
|
'pre-compact': 'pre-compact-hook.mjs',
|
|
14
14
|
'session-start': 'session-start-hook.mjs',
|
|
15
15
|
'session-end': 'session-end-hook.mjs',
|
|
16
|
+
'stop': 'stop-hook.mjs',
|
|
16
17
|
};
|
|
17
18
|
export async function handleHookCommand(hookName) {
|
|
18
19
|
const scriptFile = HOOKS[hookName];
|
|
19
20
|
if (!scriptFile) {
|
|
20
21
|
console.error(`Unknown hook: ${hookName}`);
|
|
21
22
|
console.log(`Available hooks: ${Object.keys(HOOKS).join(', ')}`);
|
|
22
|
-
console.log('Usage: claude-cortex hook <pre-compact|session-start|session-end>');
|
|
23
|
+
console.log('Usage: claude-cortex hook <pre-compact|session-start|session-end|stop>');
|
|
23
24
|
process.exit(1);
|
|
24
25
|
}
|
|
25
26
|
const scriptPath = path.join(SCRIPTS_DIR, scriptFile);
|
package/dist/setup/hooks.js.map
CHANGED
|
@@ -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;IACzC,aAAa,EAAE,sBAAsB;
|
|
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;IACrC,MAAM,EAAE,eAAe;CACxB,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,wEAAwE,CAAC,CAAC;QACtF,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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-hooks.d.ts","sourceRoot":"","sources":["../../src/setup/settings-hooks.ts"],"names":[],"mappings":"AAAA;;GAEG;AAoDH,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA0CjE"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-configure Claude Code hooks in ~/.claude/settings.json.
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
const SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json');
|
|
8
|
+
const CORTEX_HOOKS = {
|
|
9
|
+
PreCompact: {
|
|
10
|
+
hooks: [{ type: 'command', command: 'npx claude-cortex hook pre-compact', timeout: 10 }],
|
|
11
|
+
},
|
|
12
|
+
SessionStart: {
|
|
13
|
+
hooks: [{ type: 'command', command: 'npx claude-cortex hook session-start', timeout: 5 }],
|
|
14
|
+
},
|
|
15
|
+
SessionEnd: {
|
|
16
|
+
hooks: [{ type: 'command', command: 'npx claude-cortex hook session-end', timeout: 10 }],
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
const STOP_HOOK = {
|
|
20
|
+
hooks: [{ type: 'command', command: 'npx claude-cortex hook stop', timeout: 10 }],
|
|
21
|
+
};
|
|
22
|
+
function hasCortexHook(entries) {
|
|
23
|
+
return entries.some((e) => e.hooks?.some((h) => typeof h.command === 'string' && h.command.includes('claude-cortex')));
|
|
24
|
+
}
|
|
25
|
+
function readSettings() {
|
|
26
|
+
if (!fs.existsSync(SETTINGS_PATH)) {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf-8'));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function writeSettings(settings) {
|
|
37
|
+
const dir = path.dirname(SETTINGS_PATH);
|
|
38
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
39
|
+
fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
40
|
+
}
|
|
41
|
+
export function setupHooks(options) {
|
|
42
|
+
const settings = readSettings();
|
|
43
|
+
if (!settings.hooks) {
|
|
44
|
+
settings.hooks = {};
|
|
45
|
+
}
|
|
46
|
+
let added = 0;
|
|
47
|
+
// Install command hooks (PreCompact, SessionStart, SessionEnd)
|
|
48
|
+
for (const [name, entry] of Object.entries(CORTEX_HOOKS)) {
|
|
49
|
+
if (!Array.isArray(settings.hooks[name])) {
|
|
50
|
+
settings.hooks[name] = [];
|
|
51
|
+
}
|
|
52
|
+
if (!hasCortexHook(settings.hooks[name])) {
|
|
53
|
+
settings.hooks[name].push(entry);
|
|
54
|
+
added++;
|
|
55
|
+
console.log(` + Hook: ${name}`);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.log(` = Hook: ${name} (already configured)`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Optionally install Stop hook
|
|
62
|
+
if (options?.stopHook) {
|
|
63
|
+
if (!Array.isArray(settings.hooks.Stop)) {
|
|
64
|
+
settings.hooks.Stop = [];
|
|
65
|
+
}
|
|
66
|
+
if (!hasCortexHook(settings.hooks.Stop)) {
|
|
67
|
+
settings.hooks.Stop.push(STOP_HOOK);
|
|
68
|
+
added++;
|
|
69
|
+
console.log(` + Hook: Stop (opt-in)`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.log(` = Hook: Stop (already configured)`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (added > 0) {
|
|
76
|
+
writeSettings(settings);
|
|
77
|
+
console.log(`Hooks: ${added} hook(s) added to ~/.claude/settings.json`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.log('Hooks: all hooks already configured in ~/.claude/settings.json');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=settings-hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-hooks.js","sourceRoot":"","sources":["../../src/setup/settings-hooks.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Claude Cortex — Stop Hook
|
|
5
|
+
*
|
|
6
|
+
* Fires after each Claude response. Checks the last assistant message for
|
|
7
|
+
* high-salience content (decisions, fixes, learnings) that should be saved
|
|
8
|
+
* to memory via the `remember` tool.
|
|
9
|
+
*
|
|
10
|
+
* Loop prevention: If stop_hook_active is true, exits 0 immediately
|
|
11
|
+
* (programmatic check, not LLM-dependent).
|
|
12
|
+
*
|
|
13
|
+
* Exit codes:
|
|
14
|
+
* 0 = allow Claude to stop
|
|
15
|
+
* 2 = block stop (stderr tells Claude to use `remember`)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { readFileSync, existsSync } from 'fs';
|
|
19
|
+
import { homedir } from 'os';
|
|
20
|
+
|
|
21
|
+
// ==================== SALIENCE DETECTION ====================
|
|
22
|
+
|
|
23
|
+
const DECISION_KEYWORDS = [
|
|
24
|
+
'decided', 'decision', 'chose', 'chosen', 'selected', 'going with',
|
|
25
|
+
'will use', 'opted for', 'settled on', 'agreed'
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const ERROR_KEYWORDS = [
|
|
29
|
+
'fixed by', 'the fix was', 'root cause', 'the bug was', 'the solution was',
|
|
30
|
+
'resolved by', 'the issue was', 'workaround'
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const LEARNING_KEYWORDS = [
|
|
34
|
+
'learned that', 'discovered that', 'realized that', 'found out',
|
|
35
|
+
'turns out', 'TIL', 'figured out'
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const ARCHITECTURE_KEYWORDS = [
|
|
39
|
+
'architecture uses', 'design decision', 'implemented', 'refactored to',
|
|
40
|
+
'migrated from', 'created a new', 'built a'
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const PREFERENCE_KEYWORDS = [
|
|
44
|
+
'always use', 'never use', 'prefer to', 'convention is',
|
|
45
|
+
'standard is', 'rule is'
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const IMPORTANT_MARKERS = [
|
|
49
|
+
'important:', 'remember:', 'key point', 'crucial:', 'note:'
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
function detectKeywords(text, keywords) {
|
|
53
|
+
const lower = text.toLowerCase();
|
|
54
|
+
return keywords.some(kw => lower.includes(kw.toLowerCase()));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if text contains high-salience content worth remembering.
|
|
59
|
+
* Returns a description string if yes, null if no.
|
|
60
|
+
*/
|
|
61
|
+
function detectNotableContent(text) {
|
|
62
|
+
const lower = text.toLowerCase();
|
|
63
|
+
|
|
64
|
+
if (detectKeywords(lower, DECISION_KEYWORDS)) {
|
|
65
|
+
return 'A decision was made in the last response';
|
|
66
|
+
}
|
|
67
|
+
if (detectKeywords(lower, ERROR_KEYWORDS)) {
|
|
68
|
+
return 'A bug fix or error resolution was described';
|
|
69
|
+
}
|
|
70
|
+
if (detectKeywords(lower, LEARNING_KEYWORDS)) {
|
|
71
|
+
return 'Something new was learned or discovered';
|
|
72
|
+
}
|
|
73
|
+
if (detectKeywords(lower, ARCHITECTURE_KEYWORDS)) {
|
|
74
|
+
return 'An architecture or design change was made';
|
|
75
|
+
}
|
|
76
|
+
if (detectKeywords(lower, PREFERENCE_KEYWORDS)) {
|
|
77
|
+
return 'A preference or convention was stated';
|
|
78
|
+
}
|
|
79
|
+
if (detectKeywords(lower, IMPORTANT_MARKERS)) {
|
|
80
|
+
return 'Something was marked as important';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Read the last assistant message from the transcript JSONL.
|
|
88
|
+
*/
|
|
89
|
+
function getLastAssistantMessage(transcriptPath) {
|
|
90
|
+
if (!transcriptPath) return '';
|
|
91
|
+
|
|
92
|
+
const resolved = transcriptPath.replace(/^~/, homedir());
|
|
93
|
+
if (!existsSync(resolved)) return '';
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const content = readFileSync(resolved, 'utf-8');
|
|
97
|
+
const lines = content.trim().split('\n');
|
|
98
|
+
|
|
99
|
+
// Read from the end to find the last assistant message
|
|
100
|
+
for (let i = lines.length - 1; i >= Math.max(0, lines.length - 20); i--) {
|
|
101
|
+
try {
|
|
102
|
+
const entry = JSON.parse(lines[i]);
|
|
103
|
+
const role = entry.type || entry.message?.role;
|
|
104
|
+
if (role === 'assistant' && entry.message?.content) {
|
|
105
|
+
const msgContent = entry.message.content;
|
|
106
|
+
return Array.isArray(msgContent)
|
|
107
|
+
? msgContent.filter(c => c.type === 'text').map(c => c.text).join('\n')
|
|
108
|
+
: msgContent;
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
// Skip invalid lines
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return '';
|
|
115
|
+
} catch {
|
|
116
|
+
return '';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ==================== MAIN ====================
|
|
121
|
+
|
|
122
|
+
async function main() {
|
|
123
|
+
let hookData;
|
|
124
|
+
try {
|
|
125
|
+
const stdin = readFileSync(0, 'utf-8');
|
|
126
|
+
hookData = JSON.parse(stdin);
|
|
127
|
+
} catch {
|
|
128
|
+
// No stdin or invalid JSON — allow stop
|
|
129
|
+
process.exit(0);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// PROGRAMMATIC LOOP PREVENTION
|
|
133
|
+
// If Claude is already continuing from a stop hook, always allow stop.
|
|
134
|
+
if (hookData.stop_hook_active === true) {
|
|
135
|
+
process.exit(0);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Read the last assistant message
|
|
139
|
+
const lastMessage = getLastAssistantMessage(hookData.transcript_path);
|
|
140
|
+
if (!lastMessage || lastMessage.length < 50) {
|
|
141
|
+
// Too short to contain anything notable
|
|
142
|
+
process.exit(0);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Check for notable content
|
|
146
|
+
const description = detectNotableContent(lastMessage);
|
|
147
|
+
if (description) {
|
|
148
|
+
// Block stop — tell Claude to use remember
|
|
149
|
+
process.stderr.write(
|
|
150
|
+
`Use the remember tool to save what just happened: ${description}. ` +
|
|
151
|
+
`Then continue with your response.`
|
|
152
|
+
);
|
|
153
|
+
process.exit(2);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Nothing notable — allow stop
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
main().catch(() => {
|
|
161
|
+
// Never crash Claude Code
|
|
162
|
+
process.exit(0);
|
|
163
|
+
});
|