claude-mem 3.0.4 → 3.1.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.
Files changed (48) hide show
  1. package/.mcp.json +0 -11
  2. package/claude-mem +0 -0
  3. package/dist/cli.js +48 -0
  4. package/dist/commands/compress.js +10 -56
  5. package/dist/commands/install.js +167 -100
  6. package/dist/commands/load-context.js +21 -322
  7. package/dist/commands/logs.js +41 -6
  8. package/dist/commands/migrate.js +153 -62
  9. package/dist/commands/status.js +8 -25
  10. package/dist/commands/uninstall.js +1 -1
  11. package/dist/config.js +10 -0
  12. package/dist/constants.d.ts +1 -7
  13. package/dist/constants.js +25 -37
  14. package/dist/error-handler.js +29 -0
  15. package/dist/mcp-server-cli.js +0 -0
  16. package/dist/mcp-server.d.ts +8 -51
  17. package/dist/mcp-server.js +112 -156
  18. package/dist/scripts/migrate-to-simple-format.d.ts +14 -0
  19. package/dist/scripts/migrate-to-simple-format.js +140 -0
  20. package/dist/scripts/validate-migration.d.ts +17 -0
  21. package/dist/scripts/validate-migration.js +141 -0
  22. package/dist/utils/PathResolver.d.ts +12 -0
  23. package/dist/utils/PathResolver.js +46 -1
  24. package/dist/utils/TranscriptCompressor.d.ts +51 -4
  25. package/dist/utils/TranscriptCompressor.js +304 -214
  26. package/dist/utils/index.d.ts +5 -0
  27. package/dist/utils/index.js +9 -0
  28. package/dist/utils/logger.js +9 -0
  29. package/dist/utils/mcp-client.js +14 -0
  30. package/dist/utils/weaviate-mcp-adapter.d.ts +38 -10
  31. package/dist/utils/weaviate-mcp-adapter.js +250 -372
  32. package/hooks/pre-compact.js +23 -197
  33. package/hooks/session-end.js +14 -148
  34. package/hooks/session-start.js +36 -178
  35. package/hooks/shared/config-loader.js +26 -0
  36. package/package.json +1 -1
  37. package/dist/utils/HookDetector.d.ts +0 -64
  38. package/dist/utils/HookDetector.js +0 -213
  39. package/dist/utils/SettingsManager.d.ts +0 -63
  40. package/dist/utils/SettingsManager.js +0 -133
  41. package/dist/utils/common.d.ts +0 -29
  42. package/dist/utils/common.js +0 -14
  43. package/dist/utils/error-utils.d.ts +0 -93
  44. package/dist/utils/error-utils.js +0 -238
  45. package/dist/utils/mcp-client-factory.d.ts +0 -51
  46. package/dist/utils/mcp-client-factory.js +0 -115
  47. package/dist/utils/memory-mcp-client.d.ts +0 -135
  48. package/dist/utils/memory-mcp-client.js +0 -490
package/.mcp.json CHANGED
@@ -1,11 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "claude-mem": {
4
- "command": "npx",
5
- "args": [
6
- "-y",
7
- "@modelcontextprotocol/server-memory"
8
- ]
9
- }
10
- }
11
- }
package/claude-mem CHANGED
Binary file
package/dist/cli.js CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ // <Block> 1.1 ====================================
3
+ // CLI Dependencies and Imports Setup
4
+ // Natural pattern: Import what you need before using it
2
5
  import { Command } from 'commander';
3
6
  import { PACKAGE_NAME, PACKAGE_VERSION, PACKAGE_DESCRIPTION } from './config.js';
4
7
  // Import command handlers
@@ -8,11 +11,20 @@ import { uninstall } from './commands/uninstall.js';
8
11
  import { status } from './commands/status.js';
9
12
  import { logs } from './commands/logs.js';
10
13
  import { loadContext } from './commands/load-context.js';
14
+ import { migrate } from './commands/migrate.js';
11
15
  const program = new Command();
16
+ // </Block> =======================================
17
+ // <Block> 1.2 ====================================
18
+ // Program Configuration
19
+ // Natural pattern: Configure program metadata first
12
20
  program
13
21
  .name(PACKAGE_NAME)
14
22
  .description(PACKAGE_DESCRIPTION)
15
23
  .version(PACKAGE_VERSION);
24
+ // </Block> =======================================
25
+ // <Block> 1.3 ====================================
26
+ // Compress Command Definition
27
+ // Natural pattern: Define command with its options and handler
16
28
  // Compress command
17
29
  program
18
30
  .command('compress [transcript]')
@@ -21,6 +33,10 @@ program
21
33
  .option('--dry-run', 'Show what would be compressed without doing it')
22
34
  .option('-v, --verbose', 'Show detailed output')
23
35
  .action(compress);
36
+ // </Block> =======================================
37
+ // <Block> 1.4 ====================================
38
+ // Install Command Definition
39
+ // Natural pattern: Define command with its options and handler
24
40
  // Install command
25
41
  program
26
42
  .command('install')
@@ -29,6 +45,10 @@ program
29
45
  .option('--project', 'Install for current project only')
30
46
  .option('--force', 'Force installation even if already installed')
31
47
  .action(install);
48
+ // </Block> =======================================
49
+ // <Block> 1.5 ====================================
50
+ // Uninstall Command Definition
51
+ // Natural pattern: Define command with its options and handler
32
52
  // Uninstall command
33
53
  program
34
54
  .command('uninstall')
@@ -37,11 +57,19 @@ program
37
57
  .option('--project', 'Remove from project settings')
38
58
  .option('--all', 'Remove from both global and project settings')
39
59
  .action(uninstall);
60
+ // </Block> =======================================
61
+ // <Block> 1.6 ====================================
62
+ // Status Command Definition
63
+ // Natural pattern: Define command with its handler
40
64
  // Status command
41
65
  program
42
66
  .command('status')
43
67
  .description('Check installation status of Claude Memory System')
44
68
  .action(status);
69
+ // </Block> =======================================
70
+ // <Block> 1.7 ====================================
71
+ // Logs Command Definition
72
+ // Natural pattern: Define command with its options and handler
45
73
  // Logs command
46
74
  program
47
75
  .command('logs')
@@ -51,6 +79,10 @@ program
51
79
  .option('--tail [n]', 'Show last n lines', '50')
52
80
  .option('--follow', 'Follow log output')
53
81
  .action(logs);
82
+ // </Block> =======================================
83
+ // <Block> 1.8 ====================================
84
+ // Load-Context Command Definition
85
+ // Natural pattern: Define command with its options and handler
54
86
  // Load-context command
55
87
  program
56
88
  .command('load-context')
@@ -60,5 +92,21 @@ program
60
92
  .option('--raw', 'Output raw JSON instead of formatted text')
61
93
  .option('--format <type>', 'Output format: json, session-start, or default')
62
94
  .action(loadContext);
95
+ // </Block> =======================================
96
+ // <Block> 1.9 ====================================
97
+ // Migrate Command Definition
98
+ // Natural pattern: Define command with its options and handler
99
+ // Migrate command
100
+ program
101
+ .command('migrate')
102
+ .description('Migrate index to Weaviate format and extract entities')
103
+ .option('--dry-run', 'Show migration preview without performing it')
104
+ .option('--force', 'Force migration even if data exists')
105
+ .action(migrate);
106
+ // </Block> =======================================
107
+ // <Block> 1.10 ===================================
108
+ // CLI Execution
109
+ // Natural pattern: After defining all commands, parse and execute
63
110
  // Parse arguments and execute
64
111
  program.parse();
112
+ // </Block> =======================================
@@ -1,59 +1,13 @@
1
- import { existsSync } from 'fs';
2
- import { dirname, basename } from 'path';
3
- import { fileURLToPath } from 'url';
4
- const __filename = fileURLToPath(import.meta.url);
5
- const __dirname = dirname(__filename);
1
+ import { basename } from 'path';
6
2
  export async function compress(transcript, options = {}) {
7
- if (!transcript) {
8
- console.error('❌ Transcript file path is required');
9
- process.exit(1);
10
- }
11
- if (!existsSync(transcript)) {
12
- console.error(`❌ Transcript file not found: ${transcript}`);
13
- process.exit(1);
14
- }
15
3
  console.log(`🗜️ Compressing transcript: ${transcript}`);
16
- if (options.dryRun) {
17
- console.log('📋 Dry run mode - no files will be written');
18
- return;
19
- }
20
- try {
21
- // Import TranscriptCompressor dynamically to ensure it's available
22
- const { TranscriptCompressor } = await import('../utils/TranscriptCompressor.js');
23
- // Create compressor instance with debug mode based on verbose option
24
- const compressor = new TranscriptCompressor({
25
- debug: options.verbose || false
26
- });
27
- // Extract session ID from transcript filename if not provided
28
- const sessionId = options.sessionId || basename(transcript, '.jsonl');
29
- if (options.verbose) {
30
- console.log(`📊 Starting compression with session ID: ${sessionId}`);
31
- }
32
- // Perform compression
33
- const archivePath = await compressor.compress(transcript, sessionId);
34
- console.log('✅ Compression completed successfully');
35
- console.log(`📦 Archive created: ${archivePath}`);
36
- if (options.output && options.output !== dirname(archivePath)) {
37
- console.log(`📂 Output directory: ${options.output} (archive location may differ)`);
38
- }
39
- }
40
- catch (error) {
41
- const err = error;
42
- console.error('❌ Compression failed:', err.message);
43
- if (options.verbose) {
44
- console.error('Stack trace:', err.stack);
45
- }
46
- // Provide helpful error messages for common issues
47
- if (err.message.includes('Claude Code executable not found')) {
48
- console.error('💡 Hint: Make sure Claude Code is installed and accessible in your PATH');
49
- console.error(' Or set CLAUDE_CODE_PATH environment variable');
50
- }
51
- else if (err.message.includes('JWT_SECRET')) {
52
- console.error('💡 Hint: Make sure JWT_SECRET is set in your MCP configuration');
53
- }
54
- else if (err.message.includes('No summaries extracted')) {
55
- console.error('💡 Hint: The transcript may be too short or not contain analyzable content');
56
- }
57
- process.exit(1);
58
- }
4
+ // Import and run compression
5
+ const { TranscriptCompressor } = await import('../utils/TranscriptCompressor.js');
6
+ const compressor = new TranscriptCompressor({
7
+ verbose: options.verbose || false
8
+ });
9
+ const sessionId = options.sessionId || basename(transcript || '', '.jsonl');
10
+ const archivePath = await compressor.compress(transcript || '', sessionId);
11
+ console.log('✅ Compression completed successfully');
12
+ console.log(`📦 Archive created: ${archivePath}`);
59
13
  }
@@ -1,10 +1,13 @@
1
- import { readFileSync, writeFileSync, existsSync, chmodSync, mkdirSync, copyFileSync, statSync } from 'fs';
1
+ import { readFileSync, writeFileSync, existsSync, chmodSync, mkdirSync, copyFileSync, statSync, readdirSync } from 'fs';
2
2
  import { join, resolve, dirname } from 'path';
3
3
  import { homedir } from 'os';
4
+ import { execSync } from 'child_process';
4
5
  import { fileURLToPath } from 'url';
5
6
  import { PACKAGE_NAME, MCP_SERVER_SCRIPT } from '../config.js';
6
7
  import { CLI_MESSAGES } from '../constants.js';
7
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ // <Block> 2.1 ====================================
10
+ // Directory structure creation - natural setup flow
8
11
  function ensureDirectoryStructure() {
9
12
  const claudeMemDir = join(homedir(), '.claude-mem');
10
13
  const hooksDir = join(claudeMemDir, 'hooks');
@@ -17,12 +20,128 @@ function ensureDirectoryStructure() {
17
20
  }
18
21
  });
19
22
  }
23
+ // </Block> =======================================
24
+ function copyFileRecursively(src, dest) {
25
+ const stat = statSync(src);
26
+ if (stat.isDirectory()) {
27
+ if (!existsSync(dest)) {
28
+ mkdirSync(dest, { recursive: true });
29
+ }
30
+ const files = readdirSync(src);
31
+ files.forEach((file) => {
32
+ copyFileRecursively(join(src, file), join(dest, file));
33
+ });
34
+ }
35
+ else {
36
+ copyFileSync(src, dest);
37
+ }
38
+ }
39
+ function createMcpPackageJson(destDir) {
40
+ const packageJsonPath = join(destDir, 'package.json');
41
+ const mcpPackageJson = {
42
+ "name": "claude-mem-mcp-server",
43
+ "version": "1.0.0",
44
+ "description": "MCP server for claude-mem embedded Weaviate instance",
45
+ "type": "module",
46
+ "main": "mcp-server-cli.js",
47
+ "dependencies": {
48
+ "@anthropic-ai/claude-code": "^1.0.88",
49
+ "@modelcontextprotocol/sdk": "^0.5.0",
50
+ "commander": "^14.0.0",
51
+ "weaviate-ts-embedded": "^1.2.0"
52
+ },
53
+ "engines": {
54
+ "node": ">=18.0.0"
55
+ }
56
+ };
57
+ writeFileSync(packageJsonPath, JSON.stringify(mcpPackageJson, null, 2));
58
+ console.log('✅ Created package.json for MCP server');
59
+ }
60
+ function installMcpServerFiles() {
61
+ const claudeMemDir = join(homedir(), '.claude-mem');
62
+ // Find the dist directory with all compiled files
63
+ const possibleDistPaths = [
64
+ resolve(join(__dirname, '..', '..')), // Development mode - look in project root dist/
65
+ resolve(join(__dirname, '..')), // Development mode - already in dist/
66
+ resolve(join(dirname(process.execPath), '..', 'lib', 'node_modules', 'claude-mem')), // npm global install
67
+ resolve(join(dirname(process.execPath), '..', '..', 'lib', 'node_modules', 'claude-mem')), // npm global install (alternative path)
68
+ ];
69
+ let distPath = null;
70
+ for (const candidatePath of possibleDistPaths) {
71
+ const possibleDist = join(candidatePath, 'dist');
72
+ const mcpServerCliPath = join(possibleDist, MCP_SERVER_SCRIPT);
73
+ const mcpServerPath = join(possibleDist, 'mcp-server.js');
74
+ const constantsPath = join(possibleDist, 'constants.js');
75
+ const utilsPath = join(possibleDist, 'utils');
76
+ if (existsSync(mcpServerCliPath) && existsSync(mcpServerPath) && existsSync(constantsPath) && existsSync(utilsPath)) {
77
+ distPath = possibleDist;
78
+ break;
79
+ }
80
+ }
81
+ if (!distPath) {
82
+ console.log('⚠️ MCP server files not found in dist directory, MCP features will be unavailable');
83
+ return false;
84
+ }
85
+ console.log(`📁 Found MCP server files in: ${distPath}`);
86
+ // Copy all required MCP server files
87
+ const filesToCopy = [
88
+ MCP_SERVER_SCRIPT, // mcp-server-cli.js (entry point)
89
+ 'mcp-server.js', // Main server logic
90
+ 'constants.js', // Constants and messages
91
+ 'config.js', // Configuration
92
+ 'types.js', // Type definitions
93
+ 'error-handler.js' // Error handling
94
+ ];
95
+ // Copy individual files
96
+ for (const file of filesToCopy) {
97
+ const srcPath = join(distPath, file);
98
+ const destPath = join(claudeMemDir, file);
99
+ if (existsSync(srcPath)) {
100
+ copyFileSync(srcPath, destPath);
101
+ if (file === MCP_SERVER_SCRIPT) {
102
+ chmodSync(destPath, 0o755); // Make CLI executable
103
+ }
104
+ console.log(`✅ Copied ${file}`);
105
+ }
106
+ else {
107
+ console.log(`⚠️ Missing required file: ${file}`);
108
+ }
109
+ }
110
+ // Copy utils directory recursively
111
+ const utilsSrc = join(distPath, 'utils');
112
+ const utilsDest = join(claudeMemDir, 'utils');
113
+ if (existsSync(utilsSrc)) {
114
+ copyFileRecursively(utilsSrc, utilsDest);
115
+ console.log('✅ Copied utils/ directory');
116
+ }
117
+ else {
118
+ console.log('⚠️ Missing required utils/ directory');
119
+ return false;
120
+ }
121
+ // Create package.json with dependencies
122
+ createMcpPackageJson(claudeMemDir);
123
+ // Install dependencies
124
+ try {
125
+ console.log('📦 Installing MCP server dependencies...');
126
+ execSync('npm install', {
127
+ cwd: claudeMemDir,
128
+ stdio: ['ignore', 'pipe', 'pipe'],
129
+ timeout: 120000 // 2 minute timeout
130
+ });
131
+ console.log('✅ Dependencies installed successfully');
132
+ }
133
+ catch (error) {
134
+ console.log(`⚠️ Failed to install dependencies: ${error.message}`);
135
+ console.log(' You may need to run "npm install" manually in ~/.claude-mem/');
136
+ return false;
137
+ }
138
+ return true;
139
+ }
20
140
  function writeHookFiles() {
21
141
  const hooksDir = join(homedir(), '.claude-mem', 'hooks');
22
142
  const preCompactDest = join(hooksDir, 'pre-compact.js');
23
143
  const sessionStartDest = join(hooksDir, 'session-start.js');
24
144
  const sessionEndDest = join(hooksDir, 'session-end.js');
25
- const mcpServerDest = join(homedir(), '.claude-mem', MCP_SERVER_SCRIPT);
26
145
  // Find hooks directory relative to the package location
27
146
  const possibleHooksPaths = [
28
147
  resolve(join(__dirname, '..', '..', 'hooks')), // Development mode
@@ -30,18 +149,9 @@ function writeHookFiles() {
30
149
  resolve(join(dirname(process.execPath), '..', 'lib', 'node_modules', 'claude-mem', 'hooks')), // npm global install
31
150
  resolve(join(dirname(process.execPath), '..', '..', 'lib', 'node_modules', 'claude-mem', 'hooks')), // npm global install (alternative path)
32
151
  ];
33
- // Find MCP server script in dist directory
34
- const possibleMcpServerPaths = [
35
- resolve(join(__dirname, '..', '..', 'dist', MCP_SERVER_SCRIPT)), // Development mode - look in project dist/
36
- resolve(join(__dirname, '..', MCP_SERVER_SCRIPT)), // Development mode - compiled to same dist/
37
- resolve(join(dirname(process.execPath), '..', 'lib', 'node_modules', 'claude-mem', 'dist', MCP_SERVER_SCRIPT)), // npm global install
38
- resolve(join(dirname(process.execPath), '..', '..', 'lib', 'node_modules', 'claude-mem', 'dist', MCP_SERVER_SCRIPT)), // npm global install (alternative path)
39
- resolve(join(dirname(process.execPath), MCP_SERVER_SCRIPT)), // Standalone executable with script alongside
40
- ];
41
152
  let preCompactSource = null;
42
153
  let sessionStartSource = null;
43
154
  let sessionEndSource = null;
44
- let mcpServerSource = null;
45
155
  for (const hooksPath of possibleHooksPaths) {
46
156
  const preCompactCandidate = join(hooksPath, 'pre-compact.js');
47
157
  const sessionStartCandidate = join(hooksPath, 'session-start.js');
@@ -56,71 +166,37 @@ function writeHookFiles() {
56
166
  break;
57
167
  }
58
168
  }
59
- // Find MCP server script
60
- for (const mcpServerPath of possibleMcpServerPaths) {
61
- if (existsSync(mcpServerPath)) {
62
- mcpServerSource = mcpServerPath;
63
- break;
64
- }
169
+ // Proceed with whatever files we found
170
+ if (!preCompactSource || !sessionStartSource) {
171
+ console.log('⚠️ Some hook files not found, continuing with available files');
172
+ if (!preCompactSource)
173
+ console.log(' - pre-compact.js not found');
174
+ if (!sessionStartSource)
175
+ console.log(' - session-start.js not found');
65
176
  }
66
- if (preCompactSource && sessionStartSource) {
177
+ // Install available files
178
+ if (preCompactSource) {
67
179
  copyFileSync(preCompactSource, preCompactDest);
68
- copyFileSync(sessionStartSource, sessionStartDest);
69
- // Copy session-end.js if it exists
70
- if (sessionEndSource) {
71
- copyFileSync(sessionEndSource, sessionEndDest);
72
- chmodSync(sessionEndDest, 0o755);
73
- }
74
180
  chmodSync(preCompactDest, 0o755);
181
+ }
182
+ if (sessionStartSource) {
183
+ copyFileSync(sessionStartSource, sessionStartDest);
75
184
  chmodSync(sessionStartDest, 0o755);
76
- // Copy MCP server script
77
- if (mcpServerSource) {
78
- copyFileSync(mcpServerSource, mcpServerDest);
79
- chmodSync(mcpServerDest, 0o755);
80
- console.log('✅ MCP server script installed');
81
- }
82
- else {
83
- console.error('❌ MCP server script not found');
84
- console.error('Searched in:');
85
- possibleMcpServerPaths.forEach(path => console.error(` - ${path}`));
86
- process.exit(1);
87
- }
88
- // Write a configuration file for hooks to know the package name
89
- const hookConfigPath = join(hooksDir, 'config.json');
90
- const hookConfig = {
91
- packageName: PACKAGE_NAME,
92
- cliCommand: PACKAGE_NAME, // The command to call (claude-mem)
93
- backend: 'weaviate' // Always use embedded Weaviate
94
- };
95
- writeFileSync(hookConfigPath, JSON.stringify(hookConfig, null, 2));
96
- console.log(CLI_MESSAGES.INSTALLATION.HOOKS_INSTALLED);
97
- // Validate MCP server script installation
98
- if (existsSync(mcpServerDest)) {
99
- try {
100
- // Test script has execute permissions by checking file stats
101
- const stats = statSync(mcpServerDest);
102
- if (stats.mode & parseInt('100', 8)) {
103
- console.log('✅ MCP server script validated');
104
- }
105
- else {
106
- console.warn('⚠️ MCP server script may not be executable');
107
- }
108
- }
109
- catch (error) {
110
- console.warn(`⚠️ MCP server script validation failed: ${error.message}`);
111
- }
112
- }
113
- else {
114
- console.error('❌ MCP server script validation failed - file not found');
115
- process.exit(1);
116
- }
117
185
  }
118
- else {
119
- console.error(CLI_MESSAGES.ERRORS.HOOKS_NOT_FOUND);
120
- console.error('Searched in:');
121
- possibleHooksPaths.forEach(path => console.error(` - ${path}`));
122
- process.exit(1);
186
+ // Copy session-end.js if it exists
187
+ if (sessionEndSource) {
188
+ copyFileSync(sessionEndSource, sessionEndDest);
189
+ chmodSync(sessionEndDest, 0o755);
123
190
  }
191
+ // Write a configuration file for hooks to know the package name
192
+ const hookConfigPath = join(hooksDir, 'config.json');
193
+ const hookConfig = {
194
+ packageName: PACKAGE_NAME,
195
+ cliCommand: PACKAGE_NAME, // The command to call (claude-mem)
196
+ backend: 'weaviate' // Always use embedded Weaviate
197
+ };
198
+ writeFileSync(hookConfigPath, JSON.stringify(hookConfig, null, 2));
199
+ console.log(CLI_MESSAGES.INSTALLATION.HOOKS_INSTALLED);
124
200
  }
125
201
  /**
126
202
  * 🔒 LOCKED by @docs-agent | Change to 🔑 to allow @docs-agent edits
@@ -211,6 +287,8 @@ export async function install(options = {}) {
211
287
  'user';
212
288
  // Configure MCP server with the appropriate scope
213
289
  configureMcpServer(settingsDir, scope);
290
+ // Install MCP server files to ~/.claude-mem/
291
+ const mcpServerInstalled = installMcpServerFiles();
214
292
  // Write hook files to ~/.claude-mem/hooks/
215
293
  writeHookFiles();
216
294
  // Point settings to the installed hooks
@@ -226,6 +304,7 @@ export async function install(options = {}) {
226
304
  }
227
305
  catch (error) {
228
306
  console.log(`⚠️ Creating new settings file (could not parse existing): ${error.message}`);
307
+ settings = {}; // Continue with fresh settings
229
308
  }
230
309
  }
231
310
  // Ensure settings directory exists
@@ -236,36 +315,24 @@ export async function install(options = {}) {
236
315
  if (!settings.hooks) {
237
316
  settings.hooks = {};
238
317
  }
239
- // Check for existing hooks from this package
240
- // Non-tool hooks structure: array of { hooks: [...] } (no matcher field)
241
- const hasExistingPreCompact = settings.hooks.PreCompact?.some((config) => config.hooks?.some((hook) => hook.command?.includes(PACKAGE_NAME) || hook.command?.includes('pre-compact.js')));
242
- const hasExistingSessionStart = settings.hooks.SessionStart?.some((config) => config.hooks?.some((hook) => hook.command?.includes(PACKAGE_NAME) || hook.command?.includes('session-start.js')));
243
- const hasExistingSessionEnd = settings.hooks.SessionEnd?.some((config) => config.hooks?.some((hook) => hook.command?.includes(PACKAGE_NAME) || hook.command?.includes('session-end.js')));
244
- if ((hasExistingPreCompact || hasExistingSessionStart || hasExistingSessionEnd) && !options.force) {
245
- console.log(CLI_MESSAGES.INSTALLATION.ALREADY_INSTALLED);
246
- console.log(CLI_MESSAGES.INSTALLATION.USE_FORCE);
247
- return;
248
- }
249
- // Remove existing claude-mem hooks if forcing
250
- if (options.force) {
251
- // Non-tool hooks: filter out configs where hooks contain our commands
252
- if (settings.hooks.PreCompact) {
253
- settings.hooks.PreCompact = settings.hooks.PreCompact.filter((config) => !config.hooks?.some((hook) => hook.command?.includes(PACKAGE_NAME) || hook.command?.includes('pre-compact.js')));
254
- if (settings.hooks.PreCompact.length === 0) {
255
- delete settings.hooks.PreCompact;
256
- }
318
+ // Remove existing claude-mem hooks to ensure clean installation/update
319
+ // Non-tool hooks: filter out configs where hooks contain our commands
320
+ if (settings.hooks.PreCompact) {
321
+ settings.hooks.PreCompact = settings.hooks.PreCompact.filter((config) => !config.hooks?.some((hook) => hook.command?.includes(PACKAGE_NAME) || hook.command?.includes('pre-compact.js')));
322
+ if (settings.hooks.PreCompact.length === 0) {
323
+ delete settings.hooks.PreCompact;
257
324
  }
258
- if (settings.hooks.SessionStart) {
259
- settings.hooks.SessionStart = settings.hooks.SessionStart.filter((config) => !config.hooks?.some((hook) => hook.command?.includes(PACKAGE_NAME) || hook.command?.includes('session-start.js')));
260
- if (settings.hooks.SessionStart.length === 0) {
261
- delete settings.hooks.SessionStart;
262
- }
325
+ }
326
+ if (settings.hooks.SessionStart) {
327
+ settings.hooks.SessionStart = settings.hooks.SessionStart.filter((config) => !config.hooks?.some((hook) => hook.command?.includes(PACKAGE_NAME) || hook.command?.includes('session-start.js')));
328
+ if (settings.hooks.SessionStart.length === 0) {
329
+ delete settings.hooks.SessionStart;
263
330
  }
264
- if (settings.hooks.SessionEnd) {
265
- settings.hooks.SessionEnd = settings.hooks.SessionEnd.filter((config) => !config.hooks?.some((hook) => hook.command?.includes(PACKAGE_NAME) || hook.command?.includes('session-end.js')));
266
- if (settings.hooks.SessionEnd.length === 0) {
267
- delete settings.hooks.SessionEnd;
268
- }
331
+ }
332
+ if (settings.hooks.SessionEnd) {
333
+ settings.hooks.SessionEnd = settings.hooks.SessionEnd.filter((config) => !config.hooks?.some((hook) => hook.command?.includes(PACKAGE_NAME) || hook.command?.includes('session-end.js')));
334
+ if (settings.hooks.SessionEnd.length === 0) {
335
+ delete settings.hooks.SessionEnd;
269
336
  }
270
337
  }
271
338
  /**
@@ -366,7 +433,7 @@ export async function install(options = {}) {
366
433
  });
367
434
  }
368
435
  catch (error) {
369
- console.error(CLI_MESSAGES.ERRORS.SETTINGS_WRITE_FAILED(settingsPath, error.message));
370
- process.exit(1);
436
+ console.log(CLI_MESSAGES.ERRORS.SETTINGS_WRITE_FAILED(settingsPath, error.message));
437
+ console.log('⚠️ Installation completed but settings file could not be written');
371
438
  }
372
439
  }