claude-cortex 1.7.2 → 1.8.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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/setup/claude-md.d.ts +6 -2
- package/dist/setup/claude-md.d.ts.map +1 -1
- package/dist/setup/claude-md.js +34 -13
- package/dist/setup/claude-md.js.map +1 -1
- package/dist/setup/clawdbot.d.ts +15 -0
- package/dist/setup/clawdbot.d.ts.map +1 -0
- package/dist/setup/clawdbot.js +118 -0
- package/dist/setup/clawdbot.js.map +1 -0
- package/hooks/clawdbot/cortex-memory/HOOK.md +71 -0
- package/hooks/clawdbot/cortex-memory/handler.js +280 -0
- package/package.json +4 -2
- package/scripts/pre-compact-hook.mjs +671 -0
- package/scripts/pre-compact-hook.sh +29 -0
- package/scripts/session-start-hook.mjs +219 -0
- package/scripts/start-dashboard.sh +41 -0
- package/scripts/stop-dashboard.sh +21 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Pre-compact hook for Claude Memory
|
|
3
|
+
# This script runs before context compaction (manual or auto)
|
|
4
|
+
# It outputs a reminder to save important context to memory
|
|
5
|
+
|
|
6
|
+
# Read the hook input from stdin
|
|
7
|
+
INPUT=$(cat)
|
|
8
|
+
|
|
9
|
+
# Parse the trigger type (manual or auto)
|
|
10
|
+
TRIGGER=$(echo "$INPUT" | grep -o '"trigger":"[^"]*"' | cut -d'"' -f4)
|
|
11
|
+
|
|
12
|
+
# Output a system reminder that will be shown to Claude
|
|
13
|
+
# Using stderr with exit 2 shows the message to Claude
|
|
14
|
+
cat << 'EOF' >&2
|
|
15
|
+
⚠️ CONTEXT COMPACTION IMMINENT
|
|
16
|
+
|
|
17
|
+
Before this compaction completes, please use the memory tools to save any important context:
|
|
18
|
+
|
|
19
|
+
1. Key decisions made in this session
|
|
20
|
+
2. Important code patterns discovered
|
|
21
|
+
3. User preferences or requirements learned
|
|
22
|
+
4. Any pending tasks or TODOs
|
|
23
|
+
|
|
24
|
+
Use `remember` to save important items, then compaction will proceed.
|
|
25
|
+
EOF
|
|
26
|
+
|
|
27
|
+
# Exit with 0 to allow compaction to proceed
|
|
28
|
+
# (Exit 2 would block and show the message, but we want non-blocking)
|
|
29
|
+
exit 0
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Session Start Hook for Claude Memory - Auto-Recall Context
|
|
4
|
+
*
|
|
5
|
+
* This script runs when Claude Code starts a new session and:
|
|
6
|
+
* 1. Detects the current project from the working directory
|
|
7
|
+
* 2. Retrieves relevant context from memory
|
|
8
|
+
* 3. Outputs it so Claude has immediate access to project knowledge
|
|
9
|
+
*
|
|
10
|
+
* The goal: Claude always starts with relevant project context.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import Database from 'better-sqlite3';
|
|
14
|
+
import { existsSync } from 'fs';
|
|
15
|
+
import { join } from 'path';
|
|
16
|
+
import { homedir } from 'os';
|
|
17
|
+
|
|
18
|
+
// Database paths (with legacy fallback)
|
|
19
|
+
const NEW_DB_DIR = join(homedir(), '.claude-cortex');
|
|
20
|
+
const LEGACY_DB_DIR = join(homedir(), '.claude-memory');
|
|
21
|
+
|
|
22
|
+
// Auto-detect: use new path if it exists, or if legacy doesn't exist (new install)
|
|
23
|
+
function getDbPath() {
|
|
24
|
+
const newPath = join(NEW_DB_DIR, 'memories.db');
|
|
25
|
+
const legacyPath = join(LEGACY_DB_DIR, 'memories.db');
|
|
26
|
+
if (existsSync(newPath) || !existsSync(legacyPath)) {
|
|
27
|
+
return { dir: NEW_DB_DIR, path: newPath };
|
|
28
|
+
}
|
|
29
|
+
return { dir: LEGACY_DB_DIR, path: legacyPath };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { dir: DB_DIR, path: DB_PATH } = getDbPath();
|
|
33
|
+
|
|
34
|
+
// Configuration
|
|
35
|
+
const MAX_CONTEXT_MEMORIES = 15;
|
|
36
|
+
const MIN_SALIENCE_THRESHOLD = 0.3;
|
|
37
|
+
|
|
38
|
+
// ==================== PROJECT DETECTION (Mirrors src/context/project-context.ts) ====================
|
|
39
|
+
|
|
40
|
+
const SKIP_DIRECTORIES = [
|
|
41
|
+
'src', 'lib', 'dist', 'build', 'out',
|
|
42
|
+
'node_modules', '.git', '.next', '.cache',
|
|
43
|
+
'test', 'tests', '__tests__', 'spec',
|
|
44
|
+
'bin', 'scripts', 'config', 'public', 'static',
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
function extractProjectFromPath(path) {
|
|
48
|
+
if (!path) return null;
|
|
49
|
+
|
|
50
|
+
const segments = path.split(/[/\\]/).filter(Boolean);
|
|
51
|
+
if (segments.length === 0) return null;
|
|
52
|
+
|
|
53
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
54
|
+
const segment = segments[i];
|
|
55
|
+
if (!SKIP_DIRECTORIES.includes(segment.toLowerCase())) {
|
|
56
|
+
if (segment.startsWith('.')) continue;
|
|
57
|
+
return segment;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ==================== CONTEXT RETRIEVAL ====================
|
|
65
|
+
|
|
66
|
+
function getProjectContext(db, project) {
|
|
67
|
+
const memories = [];
|
|
68
|
+
|
|
69
|
+
// Get high-priority memories for this project
|
|
70
|
+
const highPriority = db.prepare(`
|
|
71
|
+
SELECT id, title, content, category, type, salience, tags, created_at
|
|
72
|
+
FROM memories
|
|
73
|
+
WHERE (project = ? OR project IS NULL)
|
|
74
|
+
AND salience >= ?
|
|
75
|
+
AND type IN ('long_term', 'episodic')
|
|
76
|
+
ORDER BY salience DESC, last_accessed DESC
|
|
77
|
+
LIMIT ?
|
|
78
|
+
`).all(project, MIN_SALIENCE_THRESHOLD, MAX_CONTEXT_MEMORIES);
|
|
79
|
+
|
|
80
|
+
memories.push(...highPriority);
|
|
81
|
+
|
|
82
|
+
// If we don't have enough, get recent memories too
|
|
83
|
+
if (memories.length < 5) {
|
|
84
|
+
const recent = db.prepare(`
|
|
85
|
+
SELECT id, title, content, category, type, salience, tags, created_at
|
|
86
|
+
FROM memories
|
|
87
|
+
WHERE (project = ? OR project IS NULL)
|
|
88
|
+
AND id NOT IN (${memories.map(m => m.id).join(',') || '0'})
|
|
89
|
+
ORDER BY created_at DESC
|
|
90
|
+
LIMIT ?
|
|
91
|
+
`).all(project, 5 - memories.length);
|
|
92
|
+
|
|
93
|
+
memories.push(...recent);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return memories;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function formatContext(memories, project) {
|
|
100
|
+
if (memories.length === 0) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const lines = [
|
|
105
|
+
`# Project Context: ${project}`,
|
|
106
|
+
'',
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
// Group by category
|
|
110
|
+
const byCategory = {};
|
|
111
|
+
for (const mem of memories) {
|
|
112
|
+
const cat = mem.category || 'note';
|
|
113
|
+
if (!byCategory[cat]) byCategory[cat] = [];
|
|
114
|
+
byCategory[cat].push(mem);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Priority order for categories
|
|
118
|
+
const categoryOrder = ['architecture', 'pattern', 'preference', 'error', 'context', 'learning', 'note', 'todo'];
|
|
119
|
+
|
|
120
|
+
for (const cat of categoryOrder) {
|
|
121
|
+
if (!byCategory[cat] || byCategory[cat].length === 0) continue;
|
|
122
|
+
|
|
123
|
+
const categoryTitle = cat.charAt(0).toUpperCase() + cat.slice(1);
|
|
124
|
+
lines.push(`## ${categoryTitle}`);
|
|
125
|
+
|
|
126
|
+
for (const mem of byCategory[cat]) {
|
|
127
|
+
const salience = Math.round(mem.salience * 100);
|
|
128
|
+
lines.push(`- **${mem.title}** (${salience}% salience)`);
|
|
129
|
+
// Truncate long content
|
|
130
|
+
const content = mem.content.length > 200
|
|
131
|
+
? mem.content.slice(0, 200) + '...'
|
|
132
|
+
: mem.content;
|
|
133
|
+
lines.push(` ${content}`);
|
|
134
|
+
}
|
|
135
|
+
lines.push('');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return lines.join('\n');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ==================== MAIN HOOK LOGIC ====================
|
|
142
|
+
|
|
143
|
+
let input = '';
|
|
144
|
+
process.stdin.setEncoding('utf8');
|
|
145
|
+
|
|
146
|
+
process.stdin.on('readable', () => {
|
|
147
|
+
let chunk;
|
|
148
|
+
while ((chunk = process.stdin.read()) !== null) {
|
|
149
|
+
input += chunk;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
process.stdin.on('end', () => {
|
|
154
|
+
try {
|
|
155
|
+
const hookData = JSON.parse(input || '{}');
|
|
156
|
+
const project = extractProjectFromPath(hookData.cwd || process.cwd());
|
|
157
|
+
|
|
158
|
+
if (!project) {
|
|
159
|
+
// No project detected, skip context retrieval
|
|
160
|
+
process.exit(0);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Check if database exists
|
|
164
|
+
if (!existsSync(DB_PATH)) {
|
|
165
|
+
console.error('[session-start] Memory database not found, skipping context retrieval');
|
|
166
|
+
process.exit(0);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Connect to database (read-only to avoid contention)
|
|
170
|
+
// timeout: 5000ms to handle busy database during concurrent access
|
|
171
|
+
const db = new Database(DB_PATH, { readonly: true, timeout: 5000 });
|
|
172
|
+
|
|
173
|
+
// Get project context
|
|
174
|
+
const memories = getProjectContext(db, project);
|
|
175
|
+
const context = formatContext(memories, project);
|
|
176
|
+
|
|
177
|
+
db.close();
|
|
178
|
+
|
|
179
|
+
// Proactive memory instructions
|
|
180
|
+
const proactiveInstructions = `
|
|
181
|
+
## IMPORTANT: Proactive Memory Use
|
|
182
|
+
|
|
183
|
+
You have access to a persistent memory system. Use it proactively:
|
|
184
|
+
|
|
185
|
+
**ALWAYS use \`remember\` immediately when:**
|
|
186
|
+
- Making architecture/design decisions
|
|
187
|
+
- Fixing bugs (capture the root cause and solution)
|
|
188
|
+
- Learning something new about the codebase
|
|
189
|
+
- User states a preference
|
|
190
|
+
- Completing significant features
|
|
191
|
+
|
|
192
|
+
**Don't wait** - call \`remember\` right after the event, not at the end of the session.
|
|
193
|
+
`;
|
|
194
|
+
|
|
195
|
+
if (context) {
|
|
196
|
+
// Output context to stdout - this will be shown to Claude
|
|
197
|
+
console.log(`
|
|
198
|
+
🧠 CLAUDE CORTEX - Project "${project}"
|
|
199
|
+
|
|
200
|
+
${context}
|
|
201
|
+
${proactiveInstructions}
|
|
202
|
+
`);
|
|
203
|
+
console.error(`[claude-cortex] Session start: loaded ${memories.length} memories for "${project}"`);
|
|
204
|
+
} else {
|
|
205
|
+
console.log(`
|
|
206
|
+
🧠 CLAUDE CORTEX - New project "${project}"
|
|
207
|
+
|
|
208
|
+
No stored memories yet for this project.
|
|
209
|
+
${proactiveInstructions}
|
|
210
|
+
`);
|
|
211
|
+
console.error(`[claude-cortex] Session start: no memories found for "${project}"`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
process.exit(0);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error(`[session-start] Error: ${error.message}`);
|
|
217
|
+
process.exit(0); // Don't block session start on errors
|
|
218
|
+
}
|
|
219
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Claude Memory Dashboard Startup Script
|
|
3
|
+
# Starts both the API server and Next.js dashboard
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
7
|
+
LOG_DIR="$HOME/.claude-memory/logs"
|
|
8
|
+
|
|
9
|
+
# Create log directory
|
|
10
|
+
mkdir -p "$LOG_DIR"
|
|
11
|
+
|
|
12
|
+
# Kill any existing instances
|
|
13
|
+
pkill -f "node.*visualization-server" 2>/dev/null
|
|
14
|
+
pkill -f "next-server.*3030" 2>/dev/null
|
|
15
|
+
|
|
16
|
+
# Wait for processes to stop
|
|
17
|
+
sleep 1
|
|
18
|
+
|
|
19
|
+
# Start API server (port 3001)
|
|
20
|
+
cd "$PROJECT_DIR"
|
|
21
|
+
nohup npm run dev:api > "$LOG_DIR/api-server.log" 2>&1 &
|
|
22
|
+
API_PID=$!
|
|
23
|
+
echo "API server started (PID: $API_PID)"
|
|
24
|
+
|
|
25
|
+
# Wait for API to be ready
|
|
26
|
+
sleep 2
|
|
27
|
+
|
|
28
|
+
# Start Next.js dashboard (port 3030)
|
|
29
|
+
cd "$PROJECT_DIR/dashboard"
|
|
30
|
+
nohup npm run dev > "$LOG_DIR/dashboard.log" 2>&1 &
|
|
31
|
+
DASHBOARD_PID=$!
|
|
32
|
+
echo "Dashboard started (PID: $DASHBOARD_PID)"
|
|
33
|
+
|
|
34
|
+
# Save PIDs for later shutdown
|
|
35
|
+
echo "$API_PID" > "$LOG_DIR/api-server.pid"
|
|
36
|
+
echo "$DASHBOARD_PID" > "$LOG_DIR/dashboard.pid"
|
|
37
|
+
|
|
38
|
+
echo "Claude Memory Dashboard running:"
|
|
39
|
+
echo " - API: http://localhost:3001"
|
|
40
|
+
echo " - Dashboard: http://localhost:3030"
|
|
41
|
+
echo " - Logs: $LOG_DIR"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Stop Claude Memory Dashboard
|
|
3
|
+
|
|
4
|
+
LOG_DIR="$HOME/.claude-memory/logs"
|
|
5
|
+
|
|
6
|
+
# Kill by PID files if they exist
|
|
7
|
+
if [ -f "$LOG_DIR/api-server.pid" ]; then
|
|
8
|
+
kill $(cat "$LOG_DIR/api-server.pid") 2>/dev/null
|
|
9
|
+
rm "$LOG_DIR/api-server.pid"
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
if [ -f "$LOG_DIR/dashboard.pid" ]; then
|
|
13
|
+
kill $(cat "$LOG_DIR/dashboard.pid") 2>/dev/null
|
|
14
|
+
rm "$LOG_DIR/dashboard.pid"
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Also kill by process name as fallback
|
|
18
|
+
pkill -f "node.*visualization-server" 2>/dev/null
|
|
19
|
+
pkill -f "next-server.*3030" 2>/dev/null
|
|
20
|
+
|
|
21
|
+
echo "Claude Memory Dashboard stopped"
|