claude-self-reflect 2.8.2 → 2.8.3

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
@@ -126,7 +126,7 @@ Files are categorized by age and processed with priority queuing to ensure newes
126
126
 
127
127
  - **Docker Desktop** (macOS/Windows) or **Docker Engine** (Linux)
128
128
  - **Node.js** 16+ (for the setup wizard)
129
- - **Claude Desktop** app
129
+ - **Claude Code** CLI
130
130
 
131
131
  ## 📖 Documentation
132
132
 
@@ -231,6 +231,98 @@ docker compose run --rm importer python /app/scripts/delta-metadata-update-safe.
231
231
 
232
232
  </details>
233
233
 
234
+ ## 🔧 Troubleshooting
235
+
236
+ ### Common Issues and Solutions
237
+
238
+ #### 1. "No collections created" after import
239
+ **Symptom**: Import runs but Qdrant shows no collections
240
+ **Cause**: Docker can't access Claude projects directory
241
+ **Solution**:
242
+ ```bash
243
+ # Run diagnostics to identify the issue
244
+ claude-self-reflect doctor
245
+
246
+ # Fix: Re-run setup to set correct paths
247
+ claude-self-reflect setup
248
+
249
+ # Verify .env has full paths (no ~):
250
+ cat .env | grep CLAUDE_LOGS_PATH
251
+ # Should show: CLAUDE_LOGS_PATH=/Users/YOUR_NAME/.claude/projects
252
+ ```
253
+
254
+ #### 2. MCP server shows "ERROR" but it's actually INFO
255
+ **Symptom**: `[ERROR] MCP server "claude-self-reflect" Server stderr: INFO Starting MCP server`
256
+ **Cause**: Claude Code displays all stderr output as errors
257
+ **Solution**: This is not an actual error - the MCP is working correctly. The INFO message confirms successful startup.
258
+
259
+ #### 3. "No JSONL files found"
260
+ **Symptom**: Setup can't find any conversation files
261
+ **Cause**: Claude Code hasn't been used yet or stores files elsewhere
262
+ **Solution**:
263
+ ```bash
264
+ # Check if files exist
265
+ ls ~/.claude/projects/
266
+
267
+ # If empty, use Claude Code to create some conversations first
268
+ # The watcher will import them automatically
269
+ ```
270
+
271
+ #### 4. Docker volume mount issues
272
+ **Symptom**: Import fails with permission errors
273
+ **Cause**: Docker can't access home directory
274
+ **Solution**:
275
+ ```bash
276
+ # Ensure Docker has file sharing permissions
277
+ # macOS: Docker Desktop → Settings → Resources → File Sharing
278
+ # Add: /Users/YOUR_USERNAME/.claude
279
+
280
+ # Restart Docker and re-run setup
281
+ docker compose down
282
+ claude-self-reflect setup
283
+ ```
284
+
285
+ #### 5. Qdrant not accessible
286
+ **Symptom**: Can't connect to localhost:6333
287
+ **Solution**:
288
+ ```bash
289
+ # Start services
290
+ docker compose --profile mcp up -d
291
+
292
+ # Check if running
293
+ docker compose ps
294
+
295
+ # View logs for errors
296
+ docker compose logs qdrant
297
+ ```
298
+
299
+ ### Diagnostic Tools
300
+
301
+ Run comprehensive diagnostics:
302
+ ```bash
303
+ claude-self-reflect doctor
304
+ ```
305
+
306
+ This checks:
307
+ - Docker installation and configuration
308
+ - Environment variables and paths
309
+ - Claude projects and JSONL files
310
+ - Import status and collections
311
+ - Service health
312
+
313
+ ### Still Having Issues?
314
+
315
+ 1. Check the logs:
316
+ ```bash
317
+ docker compose logs -f
318
+ ```
319
+
320
+ 2. Report issues with diagnostic output:
321
+ ```bash
322
+ claude-self-reflect doctor > diagnostic.txt
323
+ ```
324
+ Then include diagnostic.txt when [reporting issues](https://github.com/ramakay/claude-self-reflect/issues)
325
+
234
326
  ## 👥 Contributors
235
327
 
236
328
  Special thanks to our contributors:
package/installer/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { spawn } from 'child_process';
3
+ import { spawn, execSync } from 'child_process';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { dirname, join } from 'path';
6
6
  import fs from 'fs/promises';
@@ -76,71 +76,76 @@ async function status() {
76
76
  }
77
77
 
78
78
  async function doctor() {
79
- console.log('🔍 Checking Claude Self-Reflect installation...\n');
79
+ console.log('🔍 Running comprehensive diagnostics...\n');
80
80
 
81
- const checks = [
82
- {
83
- name: 'Python 3.10+',
84
- check: async () => {
85
- try {
86
- const { execSync } = await import('child_process');
87
- const version = execSync('python3 --version').toString().trim();
88
- return { passed: true, message: version };
89
- } catch {
90
- return { passed: false, message: 'Python 3.10+ not found' };
91
- }
81
+ // Use the new Python doctor script for comprehensive checks
82
+ const doctorScript = join(__dirname, '..', 'scripts', 'doctor.py');
83
+
84
+ try {
85
+ // Check if Python is available
86
+ try {
87
+ execSync('python3 --version', { stdio: 'ignore' });
88
+ } catch {
89
+ console.log('❌ Python 3 is required but not found');
90
+ console.log(' Please install Python 3.10 or later');
91
+ process.exit(1);
92
+ }
93
+
94
+ // Run the doctor script
95
+ const child = spawn('python3', [doctorScript], {
96
+ stdio: 'inherit',
97
+ cwd: join(__dirname, '..')
98
+ });
99
+
100
+ child.on('exit', (code) => {
101
+ if (code === 0) {
102
+ console.log('\n✅ All checks passed!');
103
+ } else if (code === 2) {
104
+ console.log('\n⚠️ Some issues found - see recommendations above');
105
+ } else {
106
+ console.log('\n❌ Critical issues found - please address them first');
92
107
  }
93
- },
94
- {
95
- name: 'Qdrant',
96
- check: async () => {
97
- try {
98
- const response = await fetch('http://localhost:6333');
99
- const data = await response.json();
100
- if (data.title && data.title.includes('qdrant')) {
101
- return { passed: true, message: `Qdrant ${data.version} is running on port 6333` };
108
+ process.exit(code || 0);
109
+ });
110
+ } catch (error) {
111
+ // Fallback to basic checks if doctor script fails
112
+ console.log('⚠️ Could not run comprehensive diagnostics, using basic checks...\n');
113
+
114
+ const checks = [
115
+ {
116
+ name: 'Docker',
117
+ check: async () => {
118
+ try {
119
+ execSync('docker info', { stdio: 'ignore' });
120
+ return { passed: true, message: 'Docker is running' };
121
+ } catch {
122
+ return { passed: false, message: 'Docker not running or not installed' };
102
123
  }
103
- } catch {}
104
- return { passed: false, message: 'Qdrant not accessible on localhost:6333' };
105
- }
106
- },
107
- {
108
- name: 'MCP Server',
109
- check: async () => {
110
- const mcpPath = join(__dirname, '..', 'mcp-server', 'pyproject.toml');
111
- try {
112
- await fs.access(mcpPath);
113
- return { passed: true, message: 'MCP server files found' };
114
- } catch {
115
- return { passed: false, message: 'MCP server files not found' };
116
124
  }
117
- }
118
- },
119
- {
120
- name: 'Environment Variables',
121
- check: async () => {
122
- const envPath = join(__dirname, '..', '.env');
123
- try {
124
- const content = await fs.readFile(envPath, 'utf-8');
125
- const hasVoyageKey = content.includes('VOYAGE_KEY=') && !content.includes('VOYAGE_KEY=your-');
126
- if (hasVoyageKey) {
127
- return { passed: true, message: '.env file configured with VOYAGE_KEY' };
128
- }
129
- return { passed: false, message: '.env file missing VOYAGE_KEY' };
130
- } catch {
131
- return { passed: false, message: '.env file not found' };
125
+ },
126
+ {
127
+ name: 'Qdrant',
128
+ check: async () => {
129
+ try {
130
+ const response = await fetch('http://localhost:6333');
131
+ const data = await response.json();
132
+ if (data.title && data.title.includes('qdrant')) {
133
+ return { passed: true, message: `Qdrant ${data.version} is running` };
134
+ }
135
+ } catch {}
136
+ return { passed: false, message: 'Qdrant not accessible on localhost:6333' };
132
137
  }
133
138
  }
139
+ ];
140
+
141
+ for (const check of checks) {
142
+ const result = await check.check();
143
+ const icon = result.passed ? '✅' : '❌';
144
+ console.log(`${icon} ${check.name}: ${result.message}`);
134
145
  }
135
- ];
136
-
137
- for (const check of checks) {
138
- const result = await check.check();
139
- const icon = result.passed ? '✅' : '❌';
140
- console.log(`${icon} ${check.name}: ${result.message}`);
146
+
147
+ console.log('\n💡 Run "claude-self-reflect setup" to fix any issues');
141
148
  }
142
-
143
- console.log('\n💡 Run "claude-self-reflect setup" to fix any issues');
144
149
  }
145
150
 
146
151
  const ASCII_ART = `
@@ -191,6 +191,12 @@ async function configureEnvironment() {
191
191
  if (!envContent.includes('CONFIG_PATH=')) {
192
192
  envContent += `CONFIG_PATH=${userConfigDir}\n`;
193
193
  }
194
+ // CRITICAL: Set CLAUDE_LOGS_PATH with expanded home directory
195
+ // Docker doesn't expand ~ in volume mounts
196
+ if (!envContent.includes('CLAUDE_LOGS_PATH=')) {
197
+ const claudeLogsPath = path.join(os.homedir(), '.claude', 'projects');
198
+ envContent += `CLAUDE_LOGS_PATH=${claudeLogsPath}\n`;
199
+ }
194
200
 
195
201
  await fs.writeFile(envPath, envContent.trim() + '\n');
196
202
  console.log('✅ Environment configured');
@@ -294,30 +300,36 @@ async function startDockerServices() {
294
300
  }
295
301
 
296
302
  async function configureClaude() {
297
- console.log('\n🤖 Configuring Claude Desktop...');
303
+ console.log('\n🤖 Configuring Claude Code...');
298
304
 
299
- const mcpScript = join(projectRoot, 'mcp-server', 'run-mcp-docker.sh');
305
+ // Use the clean wrapper if running locally, Docker script for Docker mode
306
+ const isDockerMode = process.env.USE_DOCKER_MCP === 'true';
307
+ const mcpScript = isDockerMode
308
+ ? join(projectRoot, 'mcp-server', 'run-mcp-docker.sh')
309
+ : join(projectRoot, 'mcp-server', 'run-mcp-clean.sh');
300
310
 
301
- // Create a script that runs the MCP server in Docker
302
- const scriptContent = `#!/bin/bash
311
+ if (isDockerMode) {
312
+ // Create a script that runs the MCP server in Docker
313
+ const scriptContent = `#!/bin/bash
303
314
  # Run the MCP server in the Docker container with stdin attached
304
315
  # Using python -u for unbuffered output
305
316
  # Using the main module which properly supports local embeddings
306
317
  docker exec -i claude-reflection-mcp python -u -m src
307
318
  `;
308
-
309
- await fs.writeFile(mcpScript, scriptContent, { mode: 0o755 });
319
+
320
+ await fs.writeFile(mcpScript, scriptContent, { mode: 0o755 });
321
+ }
310
322
 
311
323
  // Check if Claude CLI is available
312
324
  try {
313
325
  safeExec('which', ['claude'], { stdio: 'ignore' });
314
326
 
315
- console.log('🔧 Adding MCP to Claude Desktop...');
327
+ console.log('🔧 Adding MCP to Claude Code...');
316
328
  try {
317
329
  const mcpArgs = ['mcp', 'add', 'claude-self-reflect', mcpScript];
318
330
  safeExec('claude', mcpArgs, { stdio: 'inherit' });
319
331
  console.log('✅ MCP added successfully!');
320
- console.log('\n⚠️ Please restart Claude Desktop for changes to take effect.');
332
+ console.log('\n⚠️ Please restart Claude Code for changes to take effect.');
321
333
  } catch {
322
334
  console.log('⚠️ Could not add MCP automatically');
323
335
  showManualConfig(mcpScript);
@@ -329,7 +341,7 @@ docker exec -i claude-reflection-mcp python -u -m src
329
341
  }
330
342
 
331
343
  function showManualConfig(mcpScript) {
332
- console.log('\nAdd this to your Claude Desktop config manually:');
344
+ console.log('\nAdd this to your Claude Code config manually:');
333
345
  console.log('```json');
334
346
  console.log(JSON.stringify({
335
347
  "claude-self-reflect": {
@@ -342,6 +354,42 @@ function showManualConfig(mcpScript) {
342
354
  async function importConversations() {
343
355
  console.log('\n📚 Checking conversation baseline...');
344
356
 
357
+ // First check if Claude projects directory exists and has JSONL files
358
+ const claudeProjectsDir = path.join(os.homedir(), '.claude', 'projects');
359
+ let totalJsonlFiles = 0;
360
+ let projectCount = 0;
361
+
362
+ try {
363
+ if (fsSync.existsSync(claudeProjectsDir)) {
364
+ const projects = fsSync.readdirSync(claudeProjectsDir);
365
+ for (const project of projects) {
366
+ const projectPath = path.join(claudeProjectsDir, project);
367
+ if (fsSync.statSync(projectPath).isDirectory()) {
368
+ const jsonlFiles = fsSync.readdirSync(projectPath).filter(f => f.endsWith('.jsonl'));
369
+ if (jsonlFiles.length > 0) {
370
+ projectCount++;
371
+ totalJsonlFiles += jsonlFiles.length;
372
+ }
373
+ }
374
+ }
375
+ }
376
+
377
+ if (totalJsonlFiles === 0) {
378
+ console.log('\n⚠️ No Claude conversation files found!');
379
+ console.log(` Checked: ${claudeProjectsDir}`);
380
+ console.log('\n This could mean:');
381
+ console.log(' • You haven\'t used Claude Code yet');
382
+ console.log(' • Claude stores conversations in a different location');
383
+ console.log(' • Permissions issue accessing the directory');
384
+ console.log('\n The watcher will monitor for new conversations.');
385
+ return;
386
+ }
387
+
388
+ console.log(`✅ Found ${totalJsonlFiles} conversation files across ${projectCount} projects`);
389
+ } catch (e) {
390
+ console.log(`⚠️ Could not access Claude projects directory: ${e.message}`);
391
+ }
392
+
345
393
  // Check if baseline exists by looking for imported files state
346
394
  const configDir = path.join(os.homedir(), '.claude-self-reflect', 'config');
347
395
  const stateFile = path.join(configDir, 'imported-files.json');
@@ -492,7 +540,7 @@ async function showFinalInstructions() {
492
540
  console.log(' • Stop all: docker compose down');
493
541
 
494
542
  console.log('\n🎯 Next Steps:');
495
- console.log('1. Restart Claude Desktop');
543
+ console.log('1. Restart Claude Code');
496
544
  console.log('2. Look for "claude-self-reflect" in the MCP tools');
497
545
  console.log('3. Try: "Search my past conversations about Python"');
498
546
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "claude-self-reflect-mcp"
3
- version = "2.7.3"
3
+ version = "2.8.3"
4
4
  description = "MCP server for Claude self-reflection with memory decay"
5
5
  # readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -6,57 +6,66 @@ Designed for <20ms execution time to support status bars and shell scripts.
6
6
 
7
7
  import json
8
8
  import time
9
+ import sys
9
10
  from pathlib import Path
10
11
  from collections import defaultdict
11
12
 
12
-
13
- def extract_project_name_from_path(file_path: str) -> str:
14
- """Extract project name from JSONL file path.
15
-
16
- Handles paths like:
17
- - ~/.claude/projects/-Users-username-projects-claude-self-reflect/file.jsonl
18
- - /logs/-Users-username-projects-n8n-builder/file.jsonl
19
- """
20
- # Get the directory name containing the JSONL file
21
- path_obj = Path(file_path)
22
- dir_name = path_obj.parent.name
23
-
24
- # Extract project name from dash-encoded path
25
- # Format: -Users-username-projects-PROJECT_NAME (PROJECT_NAME can have dashes)
26
- if dir_name.startswith('-') and 'projects' in dir_name:
27
- parts = dir_name.split('-')
28
- # Find 'projects' and take everything after it as the project name
29
- try:
30
- projects_idx = parts.index('projects')
31
- if projects_idx + 1 < len(parts):
32
- # Join all parts after 'projects' to handle multi-part project names
33
- # like "claude-self-reflect", "n8n-builder", etc.
34
- project_parts = parts[projects_idx + 1:]
35
- return '-'.join(project_parts)
36
- except ValueError:
37
- pass
38
-
39
- # Fallback: use the directory name as-is
40
- return dir_name.lstrip('-')
41
-
42
-
43
- def normalize_file_path(file_path: str) -> str:
44
- """Normalize file paths to handle Docker vs local path differences.
45
-
46
- Converts:
47
- - /logs/PROJECT_DIR/file.jsonl -> ~/.claude/projects/PROJECT_DIR/file.jsonl
48
- - Already normalized paths remain unchanged
49
- """
50
- if file_path.startswith("/logs/"):
51
- # Convert Docker path to local path
52
- projects_dir = str(Path.home() / ".claude" / "projects")
53
- return file_path.replace("/logs/", projects_dir + "/", 1)
54
- return file_path
13
+ # Try to import shared utilities
14
+ try:
15
+ # Add scripts directory to path
16
+ scripts_dir = Path(__file__).parent.parent.parent / "scripts"
17
+ if scripts_dir.exists():
18
+ sys.path.insert(0, str(scripts_dir))
19
+ from shared_utils import (
20
+ normalize_file_path,
21
+ extract_project_name_from_path,
22
+ get_claude_projects_dir,
23
+ get_csr_config_dir
24
+ )
25
+ except ImportError:
26
+ # Fallback implementations if shared_utils is not available
27
+ def extract_project_name_from_path(file_path: str) -> str:
28
+ """Extract project name from JSONL file path."""
29
+ path_obj = Path(file_path)
30
+ dir_name = path_obj.parent.name
31
+
32
+ if dir_name.startswith('-') and 'projects' in dir_name:
33
+ parts = dir_name.split('-')
34
+ try:
35
+ projects_idx = parts.index('projects')
36
+ if projects_idx + 1 < len(parts):
37
+ project_parts = parts[projects_idx + 1:]
38
+ return '-'.join(project_parts)
39
+ except ValueError:
40
+ pass
41
+
42
+ return dir_name.lstrip('-')
43
+
44
+ def normalize_file_path(file_path: str) -> str:
45
+ """Normalize file paths to handle Docker vs local path differences."""
46
+ if file_path.startswith("/logs/"):
47
+ projects_dir = str(Path.home() / ".claude" / "projects")
48
+ return file_path.replace("/logs/", projects_dir + "/", 1)
49
+ return file_path
50
+
51
+ def get_claude_projects_dir() -> Path:
52
+ """Get Claude projects directory."""
53
+ import os
54
+ if 'CLAUDE_PROJECTS_DIR' in os.environ:
55
+ return Path(os.environ['CLAUDE_PROJECTS_DIR'])
56
+ return Path.home() / ".claude" / "projects"
57
+
58
+ def get_csr_config_dir() -> Path:
59
+ """Get CSR config directory."""
60
+ import os
61
+ if 'CSR_CONFIG_DIR' in os.environ:
62
+ return Path(os.environ['CSR_CONFIG_DIR'])
63
+ return Path.home() / '.claude-self-reflect' / 'config'
55
64
 
56
65
 
57
66
  def get_watcher_status() -> dict:
58
67
  """Get streaming watcher status if available."""
59
- watcher_state_file = Path.home() / "config" / "csr-watcher.json"
68
+ watcher_state_file = get_csr_config_dir() / "csr-watcher.json"
60
69
 
61
70
  if not watcher_state_file.exists():
62
71
  return {"running": False, "status": "not configured"}
@@ -85,7 +94,7 @@ def get_status() -> dict:
85
94
  Returns:
86
95
  dict: JSON structure with overall and per-project indexing status, plus watcher status
87
96
  """
88
- projects_dir = Path.home() / ".claude" / "projects"
97
+ projects_dir = get_claude_projects_dir()
89
98
  project_stats = defaultdict(lambda: {"indexed": 0, "total": 0})
90
99
 
91
100
  # Build a mapping of normalized file paths to project names
@@ -99,18 +108,24 @@ def get_status() -> dict:
99
108
  project_stats[project_name]["total"] += 1
100
109
  file_to_project[file_str] = project_name
101
110
 
111
+ # Track which files have been counted to avoid duplicates
112
+ counted_files = set()
113
+
102
114
  # Read imported-files.json to count indexed files per project
103
- config_paths = [
104
- Path.home() / ".claude-self-reflect" / "config" / "imported-files.json",
105
- Path(__file__).parent.parent.parent / "config" / "imported-files.json",
106
- Path("/config/imported-files.json") # Docker path
107
- ]
108
-
109
- imported_files_path = None
110
- for path in config_paths:
111
- if path.exists():
112
- imported_files_path = path
113
- break
115
+ config_dir = get_csr_config_dir()
116
+ imported_files_path = config_dir / "imported-files.json"
117
+
118
+ # Fallback to other locations if not found
119
+ if not imported_files_path.exists():
120
+ config_paths = [
121
+ Path(__file__).parent.parent.parent / "config" / "imported-files.json",
122
+ Path("/config/imported-files.json") # Docker path
123
+ ]
124
+
125
+ for path in config_paths:
126
+ if path.exists():
127
+ imported_files_path = path
128
+ break
114
129
 
115
130
  if imported_files_path:
116
131
  try:
@@ -123,9 +138,10 @@ def get_status() -> dict:
123
138
  # Count all files in imported_files object (they are all fully imported)
124
139
  for file_path in imported_files.keys():
125
140
  normalized_path = normalize_file_path(file_path)
126
- if normalized_path in file_to_project:
141
+ if normalized_path in file_to_project and normalized_path not in counted_files:
127
142
  project_name = file_to_project[normalized_path]
128
143
  project_stats[project_name]["indexed"] += 1
144
+ counted_files.add(normalized_path)
129
145
 
130
146
  # Also check file_metadata for partially imported files
131
147
  file_metadata = data.get("file_metadata", {})
@@ -134,9 +150,10 @@ def get_status() -> dict:
134
150
  # Only count if not already in imported_files
135
151
  if file_path not in imported_files:
136
152
  normalized_path = normalize_file_path(file_path)
137
- if normalized_path in file_to_project:
153
+ if normalized_path in file_to_project and normalized_path not in counted_files:
138
154
  project_name = file_to_project[normalized_path]
139
155
  project_stats[project_name]["indexed"] += 1
156
+ counted_files.add(normalized_path)
140
157
 
141
158
  # Also check stream_position if it contains file paths
142
159
  stream_position = data.get("stream_position", {})
@@ -148,15 +165,33 @@ def get_status() -> dict:
148
165
  # Only count if not already counted
149
166
  if file_path not in imported_files:
150
167
  normalized_path = normalize_file_path(file_path)
151
- if normalized_path in file_to_project:
168
+ if normalized_path in file_to_project and normalized_path not in counted_files:
152
169
  project_name = file_to_project[normalized_path]
153
- # Only increment if not already counted
154
- if project_stats[project_name]["indexed"] < project_stats[project_name]["total"]:
155
- project_stats[project_name]["indexed"] += 1
170
+ project_stats[project_name]["indexed"] += 1
171
+ counted_files.add(normalized_path)
156
172
  except (json.JSONDecodeError, KeyError, OSError):
157
173
  # If config file is corrupted or unreadable, continue with zero indexed counts
158
174
  pass
159
175
 
176
+ # Also read csr-watcher.json to count files imported by the watcher
177
+ watcher_state_file = config_dir / "csr-watcher.json"
178
+ if watcher_state_file.exists():
179
+ try:
180
+ with open(watcher_state_file, 'r') as f:
181
+ watcher_data = json.load(f)
182
+
183
+ # Count files imported by the watcher
184
+ watcher_imports = watcher_data.get("imported_files", {})
185
+ for file_path in watcher_imports.keys():
186
+ normalized_path = normalize_file_path(file_path)
187
+ if normalized_path in file_to_project and normalized_path not in counted_files:
188
+ project_name = file_to_project[normalized_path]
189
+ project_stats[project_name]["indexed"] += 1
190
+ counted_files.add(normalized_path)
191
+ except (json.JSONDecodeError, KeyError, OSError):
192
+ # If watcher file is corrupted or unreadable, continue
193
+ pass
194
+
160
195
  # Calculate overall stats
161
196
  total_all = sum(p["total"] for p in project_stats.values())
162
197
  indexed_all = sum(p["indexed"] for p in project_stats.values())
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-self-reflect",
3
- "version": "2.8.2",
3
+ "version": "2.8.3",
4
4
  "description": "Give Claude perfect memory of all your conversations - Installation wizard for Python MCP server",
5
5
  "keywords": [
6
6
  "claude",
@@ -338,7 +338,19 @@ def main():
338
338
  logger.info(f"Loaded state with {len(state.get('imported_files', {}))} previously imported files")
339
339
 
340
340
  # Find all projects
341
- logs_dir = Path(os.getenv("LOGS_DIR", "/logs"))
341
+ # Use LOGS_DIR env var, or fall back to Claude projects directory, then /logs for Docker
342
+ logs_dir_env = os.getenv("LOGS_DIR")
343
+ if logs_dir_env:
344
+ logs_dir = Path(logs_dir_env)
345
+ elif (Path.home() / ".claude" / "projects").exists():
346
+ logs_dir = Path.home() / ".claude" / "projects"
347
+ else:
348
+ logs_dir = Path("/logs") # Docker fallback
349
+
350
+ if not logs_dir.exists():
351
+ logger.error(f"Projects directory not found: {logs_dir}")
352
+ sys.exit(1)
353
+
342
354
  project_dirs = [d for d in logs_dir.iterdir() if d.is_dir()]
343
355
  logger.info(f"Found {len(project_dirs)} projects to import")
344
356