claude-mem 2.1.2 → 3.0.2
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/claude-mem +0 -0
- package/hooks/pre-compact.js +193 -40
- package/hooks/session-end.js +135 -55
- package/hooks/session-start.js +169 -54
- package/package.json +1 -1
- package/src/claude-mem.js +859 -0
- package/hooks/hook-utils.js +0 -318
package/hooks/session-start.js
CHANGED
|
@@ -1,75 +1,190 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* Uses shared utilities to eliminate code duplication
|
|
4
|
+
* 🔒 LOCKED by @docs-agent | Change to 🔑 to allow @docs-agent edits
|
|
6
5
|
*
|
|
7
|
-
*
|
|
6
|
+
* OFFICIAL DOCS: Claude Code SessionStart Hook v2025
|
|
7
|
+
* Last Verified: 2025-08-31
|
|
8
|
+
* @see https://docs.anthropic.com/en/docs/claude-code/hooks
|
|
9
|
+
*
|
|
10
|
+
* SessionStart Hook Payload:
|
|
11
|
+
* {
|
|
12
|
+
* "session_id": "string",
|
|
13
|
+
* "transcript_path": "string",
|
|
14
|
+
* "hook_event_name": "SessionStart",
|
|
15
|
+
* "source": "startup" | "compact" | "vscode" | "web"
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* The 'source' field indicates how the session was initiated:
|
|
19
|
+
* - "startup": New session started normally
|
|
20
|
+
* - "compact": Session started after compaction
|
|
21
|
+
* - "vscode": Session initiated from VS Code
|
|
22
|
+
* - "web": Session initiated from web interface
|
|
23
|
+
*
|
|
24
|
+
* Valid Response Format:
|
|
25
|
+
* Success without context: { "continue": true }
|
|
26
|
+
* Success with context: {
|
|
27
|
+
* "continue": true,
|
|
28
|
+
* "hookSpecificOutput": {
|
|
29
|
+
* "hookEventName": "SessionStart",
|
|
30
|
+
* "additionalContext": "string" // Context to add to session
|
|
31
|
+
* }
|
|
32
|
+
* }
|
|
33
|
+
* Failure: { "continue": false, "stopReason": "error message" }
|
|
34
|
+
*
|
|
35
|
+
* @docs-ref: SessionStart supports hookSpecificOutput.additionalContext
|
|
36
|
+
* @see https://docs.anthropic.com/claude-code/hooks#sessionstart
|
|
8
37
|
*/
|
|
9
38
|
|
|
10
|
-
import {
|
|
11
|
-
import { dirname, basename } from 'path';
|
|
39
|
+
import { spawn } from 'child_process';
|
|
40
|
+
import { join, dirname, basename } from 'path';
|
|
41
|
+
import { fileURLToPath } from 'url';
|
|
42
|
+
import { readFileSync, existsSync } from 'fs';
|
|
12
43
|
|
|
13
|
-
|
|
44
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
45
|
+
|
|
46
|
+
// Load configuration to get the CLI command name
|
|
47
|
+
let cliCommand = 'claude-mem'; // Default fallback
|
|
48
|
+
const configPath = join(__dirname, 'config.json');
|
|
49
|
+
if (existsSync(configPath)) {
|
|
14
50
|
try {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
51
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
52
|
+
cliCommand = config.cliCommand || 'claude-mem';
|
|
53
|
+
} catch (e) {
|
|
54
|
+
// Fallback to default if config read fails
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function sessionStartHook() {
|
|
59
|
+
let input = '';
|
|
60
|
+
|
|
61
|
+
// Read JSON input from stdin
|
|
62
|
+
process.stdin.on('data', chunk => {
|
|
63
|
+
input += chunk;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
process.stdin.on('end', async () => {
|
|
67
|
+
try {
|
|
68
|
+
const data = JSON.parse(input);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @docs-ref: SessionStart payload includes 'source' field
|
|
72
|
+
* See: https://docs.anthropic.com/en/docs/claude-code/hooks#sessionstart
|
|
73
|
+
*
|
|
74
|
+
* The 'source' field indicates the session start context:
|
|
75
|
+
* - 'startup': New session (load context)
|
|
76
|
+
* - 'compact': Session after compaction (SHOULD load context)
|
|
77
|
+
* - 'vscode': VS Code initiated session
|
|
78
|
+
* - 'web': Web interface initiated session
|
|
79
|
+
*
|
|
80
|
+
* IMPORTANT: We always load context regardless of source.
|
|
81
|
+
* After compaction, users expect to see their compressed memories loaded.
|
|
82
|
+
* The session after compact is effectively a new session with compressed context.
|
|
83
|
+
*
|
|
84
|
+
* There is no specific indicator for /continue command in the payload,
|
|
85
|
+
* so we treat all session starts equally and load available context.
|
|
86
|
+
*/
|
|
87
|
+
// Always proceed to load context for all session types
|
|
24
88
|
|
|
25
|
-
// Extract project name from
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
89
|
+
// Extract project name from transcript path if available
|
|
90
|
+
let projectName = null;
|
|
91
|
+
if (data.transcript_path) {
|
|
92
|
+
const dir = dirname(data.transcript_path);
|
|
93
|
+
const dirName = basename(dir);
|
|
94
|
+
|
|
95
|
+
// Extract project name from directory pattern
|
|
96
|
+
if (dirName.includes('-Scripts-')) {
|
|
97
|
+
const parts = dirName.split('-Scripts-');
|
|
98
|
+
if (parts.length > 1) {
|
|
99
|
+
projectName = parts[1];
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
projectName = dirName;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Sanitize project name to match what compression uses
|
|
106
|
+
// This ensures we match the exact project name in the index
|
|
107
|
+
if (projectName) {
|
|
108
|
+
projectName = projectName.replace(/[^a-zA-Z0-9]/g, '_');
|
|
30
109
|
}
|
|
31
|
-
} else {
|
|
32
|
-
projectName = dirName;
|
|
33
110
|
}
|
|
34
111
|
|
|
35
|
-
//
|
|
112
|
+
// Build command to run
|
|
113
|
+
const args = ['load-context', '--format', 'session-start'];
|
|
114
|
+
|
|
115
|
+
// Add project filter if we have a project name
|
|
36
116
|
if (projectName) {
|
|
37
|
-
|
|
117
|
+
args.push('--project', projectName);
|
|
38
118
|
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Build command to run
|
|
42
|
-
const args = ['load-context', '--format', 'session-start'];
|
|
43
|
-
if (projectName) {
|
|
44
|
-
args.push('--project', projectName);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Use spawnCLI utility for loading context
|
|
48
|
-
const cliCommand = config.cliCommand || 'claude-mem';
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
const result = await spawnCLI(cliCommand, args);
|
|
52
119
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
120
|
+
// Call the CLI load-context command directly
|
|
121
|
+
// Use the configured CLI command name (claude-mem)
|
|
122
|
+
// This ensures we use the correct version with full summaries
|
|
123
|
+
const loader = spawn(cliCommand, args, {
|
|
124
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
let stdout = '';
|
|
128
|
+
let stderr = '';
|
|
129
|
+
|
|
130
|
+
loader.stdout.on('data', (data) => {
|
|
131
|
+
stdout += data.toString();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
loader.stderr.on('data', (data) => {
|
|
135
|
+
stderr += data.toString();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
loader.on('close', (code) => {
|
|
139
|
+
if (code !== 0) {
|
|
140
|
+
// If load-context fails, just continue without context
|
|
141
|
+
// This could happen if no index exists yet
|
|
142
|
+
// @docs-ref: Always return valid response format
|
|
143
|
+
console.log(JSON.stringify({
|
|
144
|
+
continue: true
|
|
145
|
+
}));
|
|
146
|
+
process.exit(0);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Output the formatted context for Claude to see using proper JSON format
|
|
150
|
+
// @docs-ref: SessionStart supports hookSpecificOutput.additionalContext
|
|
151
|
+
if (stdout && stdout.trim()) {
|
|
152
|
+
console.log(JSON.stringify({
|
|
153
|
+
continue: true,
|
|
154
|
+
hookSpecificOutput: {
|
|
155
|
+
hookEventName: "SessionStart",
|
|
156
|
+
additionalContext: stdout
|
|
157
|
+
}
|
|
158
|
+
}));
|
|
159
|
+
} else {
|
|
160
|
+
// No context to add, just continue
|
|
161
|
+
console.log(JSON.stringify({
|
|
162
|
+
continue: true
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
process.exit(0);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
loader.on('error', () => {
|
|
170
|
+
// If there's an error running the command, continue without context
|
|
171
|
+
// We don't want to break the session start
|
|
172
|
+
// @docs-ref: Always return valid response format
|
|
173
|
+
console.log(JSON.stringify({
|
|
174
|
+
continue: true
|
|
175
|
+
}));
|
|
176
|
+
process.exit(0);
|
|
177
|
+
});
|
|
59
178
|
|
|
60
179
|
} catch (error) {
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
console.log(JSON.stringify({
|
|
180
|
+
// Any errors, just continue without additional context
|
|
181
|
+
// @docs-ref: Always return valid response format
|
|
182
|
+
console.log(JSON.stringify({
|
|
183
|
+
continue: true
|
|
184
|
+
}));
|
|
185
|
+
process.exit(0);
|
|
64
186
|
}
|
|
65
|
-
|
|
66
|
-
process.exit(0);
|
|
67
|
-
|
|
68
|
-
} catch (error) {
|
|
69
|
-
// Any errors, just continue without additional context
|
|
70
|
-
console.log(JSON.stringify({ continue: true }));
|
|
71
|
-
process.exit(0);
|
|
72
|
-
}
|
|
187
|
+
});
|
|
73
188
|
}
|
|
74
189
|
|
|
75
190
|
sessionStartHook();
|