claude-self-reflect 2.8.4 → 2.8.6

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.
@@ -1,4 +1,11 @@
1
- FROM python:3.11-slim
1
+ FROM python:3.13-slim
2
+
3
+ # SECURITY: CVE-2025-58050 mitigation - PCRE2 heap buffer overflow
4
+ # TODO: Remove explicit PCRE2 upgrade when base image includes patched version
5
+ RUN apt-get update && \
6
+ (apt-get install -y --only-upgrade libpcre2-8-0 2>/dev/null || \
7
+ echo "Warning: PCRE2 10.46+ not yet available") && \
8
+ apt-get upgrade -y && rm -rf /var/lib/apt/lists/*
2
9
 
3
10
  # Install system dependencies
4
11
  RUN apt-get update && apt-get install -y \
@@ -1,9 +1,13 @@
1
- FROM python:3.12-slim
1
+ FROM python:3.13-slim
2
2
 
3
3
  WORKDIR /app
4
4
 
5
- # Update system packages for security
6
- RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/*
5
+ # SECURITY: CVE-2025-58050 mitigation - PCRE2 heap buffer overflow
6
+ # TODO: Remove explicit PCRE2 upgrade when base image includes patched version
7
+ RUN apt-get update && \
8
+ (apt-get install -y --only-upgrade libpcre2-8-0 2>/dev/null || \
9
+ echo "Warning: PCRE2 10.46+ not yet available") && \
10
+ apt-get upgrade -y && rm -rf /var/lib/apt/lists/*
7
11
 
8
12
  # Install dependencies directly (avoids file path issues with global npm installs)
9
13
  RUN pip install --no-cache-dir \
@@ -1,4 +1,11 @@
1
- FROM python:3.12-slim
1
+ FROM python:3.13-slim
2
+
3
+ # SECURITY: CVE-2025-58050 mitigation - PCRE2 heap buffer overflow
4
+ # TODO: Remove explicit PCRE2 upgrade when base image includes patched version
5
+ RUN apt-get update && \
6
+ (apt-get install -y --only-upgrade libpcre2-8-0 2>/dev/null || \
7
+ echo "Warning: PCRE2 10.46+ not yet available") && \
8
+ apt-get upgrade -y && rm -rf /var/lib/apt/lists/*
2
9
 
3
10
  # Update system packages for security and install curl
4
11
  RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \
@@ -1,4 +1,4 @@
1
- FROM python:3.12-alpine
1
+ FROM python:3.13-alpine
2
2
 
3
3
  # Install build dependencies, security updates and curl
4
4
  RUN apk update && apk upgrade && \
@@ -1,4 +1,4 @@
1
- FROM python:3.12-alpine
1
+ FROM python:3.13-alpine
2
2
 
3
3
  WORKDIR /app
4
4
 
@@ -1,9 +1,14 @@
1
- FROM python:3.12-slim
1
+ FROM python:3.13-slim
2
2
 
3
3
  WORKDIR /app
4
4
 
5
+ # SECURITY: CVE-2025-58050 mitigation - PCRE2 heap buffer overflow
5
6
  # Update system packages for security
6
- RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/*
7
+ # TODO: Remove explicit PCRE2 upgrade when base image includes patched version
8
+ RUN apt-get update && \
9
+ (apt-get install -y --only-upgrade libpcre2-8-0 2>/dev/null || \
10
+ echo "Warning: PCRE2 10.46+ not yet available") && \
11
+ apt-get upgrade -y && rm -rf /var/lib/apt/lists/*
7
12
 
8
13
  # Copy the MCP server package files
9
14
  COPY mcp-server/pyproject.toml ./
@@ -1,4 +1,4 @@
1
- FROM python:3.12-alpine
1
+ FROM python:3.13-alpine
2
2
 
3
3
  WORKDIR /app
4
4
 
@@ -1,4 +1,11 @@
1
- FROM python:3.12-slim
1
+ FROM python:3.13-slim
2
+
3
+ # SECURITY: CVE-2025-58050 mitigation - PCRE2 heap buffer overflow
4
+ # TODO: Remove explicit PCRE2 upgrade when base image includes patched version
5
+ RUN apt-get update && \
6
+ (apt-get install -y --only-upgrade libpcre2-8-0 2>/dev/null || \
7
+ echo "Warning: PCRE2 10.46+ not yet available") && \
8
+ apt-get upgrade -y && rm -rf /var/lib/apt/lists/*
2
9
 
3
10
  # Install system dependencies
4
11
  RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \
@@ -1,7 +1,13 @@
1
- FROM python:3.12-slim
2
-
3
- # Update system packages for security and install build dependencies
4
- RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \
1
+ FROM python:3.13-slim
2
+
3
+ # SECURITY: CVE-2025-58050 mitigation - PCRE2 heap buffer overflow
4
+ # Attempting explicit upgrade of libpcre2-8-0 (vulnerable: 10.45-1, fixed: 10.46+)
5
+ # TODO: Remove explicit PCRE2 upgrade when base image includes patched version
6
+ RUN apt-get update && \
7
+ (apt-get install -y --only-upgrade libpcre2-8-0 2>/dev/null || \
8
+ echo "Warning: PCRE2 10.46+ not yet available, continuing with security updates") && \
9
+ apt-get upgrade -y && \
10
+ apt-get install -y --no-install-recommends \
5
11
  gcc \
6
12
  g++ \
7
13
  curl \
@@ -1,4 +1,4 @@
1
- FROM python:3.12-alpine
1
+ FROM python:3.13-alpine
2
2
 
3
3
  WORKDIR /app
4
4
 
@@ -1,10 +1,14 @@
1
- FROM python:3.12-slim
1
+ FROM python:3.13-slim
2
2
 
3
+ # SECURITY: CVE-2025-58050 mitigation - PCRE2 heap buffer overflow
3
4
  # Update system packages for security and install build dependencies for psutil
4
- RUN apt-get update && apt-get upgrade -y && apt-get install -y \
5
- gcc \
6
- python3-dev \
7
- && rm -rf /var/lib/apt/lists/*
5
+ # TODO: Remove explicit PCRE2 upgrade when base image includes patched version
6
+ RUN apt-get update && \
7
+ (apt-get install -y --only-upgrade libpcre2-8-0 2>/dev/null || \
8
+ echo "Warning: PCRE2 10.46+ not yet available") && \
9
+ apt-get upgrade -y && \
10
+ apt-get install -y gcc python3-dev && \
11
+ rm -rf /var/lib/apt/lists/*
8
12
 
9
13
  # Install Python dependencies
10
14
  RUN pip install --no-cache-dir \
@@ -1,4 +1,4 @@
1
- FROM python:3.12-alpine
1
+ FROM python:3.13-alpine
2
2
 
3
3
  WORKDIR /app
4
4
 
package/README.md CHANGED
@@ -26,6 +26,21 @@ Give Claude perfect memory of all your conversations. Search past discussions in
26
26
 
27
27
  **🔒 100% Local by Default** • **⚡ Blazing Fast Search** • **🚀 Zero Configuration** • **🏭 Production Ready**
28
28
 
29
+ ## 📑 Table of Contents
30
+
31
+ - [🚀 Quick Install](#-quick-install)
32
+ - [✨ The Magic](#-the-magic)
33
+ - [📊 Before & After](#-before--after)
34
+ - [💬 Real Examples](#-real-examples)
35
+ - [🆕 NEW: Real-time Indexing Status](#-new-real-time-indexing-status-in-your-terminal)
36
+ - [🎯 Key Features](#-key-features)
37
+ - [🏗️ Architecture](#️-architecture)
38
+ - [🛠️ Requirements](#️-requirements)
39
+ - [📖 Documentation](#-documentation)
40
+ - [📦 What's New](#-whats-new)
41
+ - [🔧 Troubleshooting](#-troubleshooting)
42
+ - [👥 Contributors](#-contributors)
43
+
29
44
  ## 🚀 Quick Install
30
45
 
31
46
  ```bash
@@ -97,7 +112,9 @@ Works with [Claude Code Statusline](https://github.com/sirmalloc/ccstatusline) -
97
112
 
98
113
  ## 🎯 Key Features
99
114
 
100
- ### 📊 Statusline Integration
115
+ <details>
116
+ <summary><b>📊 Statusline Integration</b></summary>
117
+
101
118
  See your indexing progress right in your terminal! Works with [Claude Code Statusline](https://github.com/sirmalloc/ccstatusline):
102
119
  - **Progress Bar** - Visual indicator `[████████ ] 91%`
103
120
  - **Indexing Lag** - Shows backlog `• 7h behind`
@@ -106,7 +123,11 @@ See your indexing progress right in your terminal! Works with [Claude Code Statu
106
123
 
107
124
  [Learn more about statusline integration →](docs/statusline-integration.md)
108
125
 
109
- ### Project-Scoped Search
126
+ </details>
127
+
128
+ <details>
129
+ <summary><b>🔍 Project-Scoped Search</b></summary>
130
+
110
131
  Searches are **project-aware by default**. Claude automatically searches within your current project:
111
132
 
112
133
  ```
@@ -119,21 +140,37 @@ You: "Search all projects for WebSocket implementations"
119
140
  Claude: [Searches across ALL your projects]
120
141
  ```
121
142
 
122
- ### ⏱️ Memory Decay
143
+ </details>
144
+
145
+ <details>
146
+ <summary><b>⏱️ Memory Decay</b></summary>
147
+
123
148
  Recent conversations matter more. Old ones fade. Like your brain, but reliable.
149
+ - **90-day half-life**: Recent memories stay strong
150
+ - **Graceful aging**: Old information fades naturally
151
+ - **Configurable**: Adjust decay rate to your needs
152
+
153
+ </details>
154
+
155
+ <details>
156
+ <summary><b>⚡ Performance at Scale</b></summary>
124
157
 
125
- ### ⚡ Performance at Scale
126
158
  - **Search**: <3ms average response time
127
159
  - **Scale**: 600+ conversations across 24 projects
128
160
  - **Reliability**: 100% indexing success rate
129
161
  - **Memory**: 96% reduction from v2.5.15
162
+ - **Real-time**: HOT/WARM/COLD intelligent prioritization
163
+
164
+ </details>
130
165
 
131
166
  ## 🏗️ Architecture
132
167
 
168
+ <details>
169
+ <summary><b>View Architecture Diagram & Details</b></summary>
170
+
133
171
  ![Import Architecture](docs/diagrams/import-architecture.png)
134
172
 
135
- <details>
136
- <summary>🔥 HOT/WARM/COLD Intelligent Prioritization</summary>
173
+ ### 🔥 HOT/WARM/COLD Intelligent Prioritization
137
174
 
138
175
  - **🔥 HOT** (< 5 minutes): 2-second intervals for near real-time import
139
176
  - **🌡️ WARM** (< 24 hours): Normal priority with starvation prevention
@@ -141,13 +178,37 @@ Recent conversations matter more. Old ones fade. Like your brain, but reliable.
141
178
 
142
179
  Files are categorized by age and processed with priority queuing to ensure newest content gets imported quickly while preventing older files from being starved.
143
180
 
181
+ ### Components
182
+ - **Vector Database**: Qdrant for semantic search
183
+ - **MCP Server**: Python-based using FastMCP
184
+ - **Embeddings**: Local (FastEmbed) or Cloud (Voyage AI)
185
+ - **Import Pipeline**: Docker-based with automatic monitoring
186
+
144
187
  </details>
145
188
 
146
189
  ## 🛠️ Requirements
147
190
 
191
+ <details>
192
+ <summary><b>System Requirements</b></summary>
193
+
194
+ ### Minimum Requirements
148
195
  - **Docker Desktop** (macOS/Windows) or **Docker Engine** (Linux)
149
196
  - **Node.js** 16+ (for the setup wizard)
150
197
  - **Claude Code** CLI
198
+ - **4GB RAM** available for Docker
199
+ - **2GB disk space** for vector database
200
+
201
+ ### Recommended
202
+ - **8GB RAM** for optimal performance
203
+ - **SSD storage** for faster indexing
204
+ - **Docker Desktop 4.0+** for best compatibility
205
+
206
+ ### Operating Systems
207
+ - ✅ macOS 11+ (Intel & Apple Silicon)
208
+ - ✅ Windows 10/11 with WSL2
209
+ - ✅ Linux (Ubuntu 20.04+, Debian 11+)
210
+
211
+ </details>
151
212
 
152
213
  ## 📖 Documentation
153
214
 
@@ -254,9 +315,10 @@ docker compose run --rm importer python /app/scripts/delta-metadata-update-safe.
254
315
 
255
316
  ## 🔧 Troubleshooting
256
317
 
257
- ### Common Issues and Solutions
318
+ <details>
319
+ <summary><b>Common Issues and Solutions</b></summary>
258
320
 
259
- #### 1. "No collections created" after import
321
+ ### 1. "No collections created" after import
260
322
  **Symptom**: Import runs but Qdrant shows no collections
261
323
  **Cause**: Docker can't access Claude projects directory
262
324
  **Solution**:
@@ -272,12 +334,12 @@ cat .env | grep CLAUDE_LOGS_PATH
272
334
  # Should show: CLAUDE_LOGS_PATH=/Users/YOUR_NAME/.claude/projects
273
335
  ```
274
336
 
275
- #### 2. MCP server shows "ERROR" but it's actually INFO
337
+ ### 2. MCP server shows "ERROR" but it's actually INFO
276
338
  **Symptom**: `[ERROR] MCP server "claude-self-reflect" Server stderr: INFO Starting MCP server`
277
339
  **Cause**: Claude Code displays all stderr output as errors
278
340
  **Solution**: This is not an actual error - the MCP is working correctly. The INFO message confirms successful startup.
279
341
 
280
- #### 3. "No JSONL files found"
342
+ ### 3. "No JSONL files found"
281
343
  **Symptom**: Setup can't find any conversation files
282
344
  **Cause**: Claude Code hasn't been used yet or stores files elsewhere
283
345
  **Solution**:
@@ -289,7 +351,7 @@ ls ~/.claude/projects/
289
351
  # The watcher will import them automatically
290
352
  ```
291
353
 
292
- #### 4. Docker volume mount issues
354
+ ### 4. Docker volume mount issues
293
355
  **Symptom**: Import fails with permission errors
294
356
  **Cause**: Docker can't access home directory
295
357
  **Solution**:
@@ -303,7 +365,7 @@ docker compose down
303
365
  claude-self-reflect setup
304
366
  ```
305
367
 
306
- #### 5. Qdrant not accessible
368
+ ### 5. Qdrant not accessible
307
369
  **Symptom**: Can't connect to localhost:6333
308
370
  **Solution**:
309
371
  ```bash
@@ -317,9 +379,12 @@ docker compose ps
317
379
  docker compose logs qdrant
318
380
  ```
319
381
 
320
- ### Diagnostic Tools
382
+ </details>
321
383
 
322
- Run comprehensive diagnostics:
384
+ <details>
385
+ <summary><b>Diagnostic Tools</b></summary>
386
+
387
+ ### Run Comprehensive Diagnostics
323
388
  ```bash
324
389
  claude-self-reflect doctor
325
390
  ```
@@ -331,18 +396,41 @@ This checks:
331
396
  - Import status and collections
332
397
  - Service health
333
398
 
334
- ### Still Having Issues?
399
+ ### Check Logs
400
+ ```bash
401
+ # View all service logs
402
+ docker compose logs -f
403
+
404
+ # View specific service
405
+ docker compose logs qdrant
406
+ docker compose logs watcher
407
+ ```
408
+
409
+ ### Generate Diagnostic Report
410
+ ```bash
411
+ # Create diagnostic file for issue reporting
412
+ claude-self-reflect doctor > diagnostic.txt
413
+ ```
335
414
 
336
- 1. Check the logs:
337
- ```bash
338
- docker compose logs -f
339
- ```
415
+ </details>
340
416
 
341
- 2. Report issues with diagnostic output:
342
- ```bash
343
- claude-self-reflect doctor > diagnostic.txt
344
- ```
345
- Then include diagnostic.txt when [reporting issues](https://github.com/ramakay/claude-self-reflect/issues)
417
+ <details>
418
+ <summary><b>Getting Help</b></summary>
419
+
420
+ 1. **Check Documentation**
421
+ - [Troubleshooting Guide](docs/troubleshooting.md)
422
+ - [FAQ](docs/faq.md)
423
+ - [Windows Setup](docs/windows-setup.md)
424
+
425
+ 2. **Community Support**
426
+ - [GitHub Discussions](https://github.com/ramakay/claude-self-reflect/discussions)
427
+ - [Discord Community](https://discord.gg/claude-self-reflect)
428
+
429
+ 3. **Report Issues**
430
+ - [GitHub Issues](https://github.com/ramakay/claude-self-reflect/issues)
431
+ - Include diagnostic output when reporting
432
+
433
+ </details>
346
434
 
347
435
  ## 👥 Contributors
348
436
 
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Health check endpoint for Claude Self-Reflect system
4
+
5
+ Provides a simple way to monitor system health and import status.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import sys
11
+ from pathlib import Path
12
+ from datetime import datetime, timedelta
13
+ from typing import Dict, Any
14
+
15
+ # No sys.path modification needed - using subprocess for imports
16
+
17
+ def check_qdrant_health() -> Dict[str, Any]:
18
+ """Check if Qdrant is accessible and has data"""
19
+ try:
20
+ from qdrant_client import QdrantClient
21
+ from qdrant_client.http.exceptions import ResponseHandlingException
22
+
23
+ # Add timeout for network operations
24
+ client = QdrantClient(
25
+ 'localhost',
26
+ port=6333,
27
+ timeout=5 # 5 second timeout
28
+ )
29
+
30
+ collections = client.get_collections().collections
31
+
32
+ return {
33
+ 'status': 'healthy',
34
+ 'collections': len(collections),
35
+ 'accessible': True
36
+ }
37
+ except (ResponseHandlingException, ConnectionError, TimeoutError) as e:
38
+ # Sanitize error messages to avoid information disclosure
39
+ return {
40
+ 'status': 'unhealthy',
41
+ 'error': 'Connection failed',
42
+ 'accessible': False
43
+ }
44
+ except Exception as e:
45
+ return {
46
+ 'status': 'unhealthy',
47
+ 'error': 'Service unavailable',
48
+ 'accessible': False
49
+ }
50
+
51
+ def check_import_status() -> Dict[str, Any]:
52
+ """Check import status from status.py"""
53
+ try:
54
+ # Run status.py and parse output
55
+ import subprocess
56
+ import json
57
+
58
+ result = subprocess.run(
59
+ [sys.executable, str(Path(__file__).parent / "status.py")],
60
+ capture_output=True,
61
+ text=True,
62
+ timeout=10
63
+ )
64
+
65
+ if result.returncode == 0:
66
+ status = json.loads(result.stdout)
67
+ return {
68
+ 'percentage': status['overall']['percentage'],
69
+ 'indexed': status['overall']['indexed'],
70
+ 'total': status['overall']['total'],
71
+ 'backlog': status['overall']['backlog']
72
+ }
73
+ else:
74
+ return {
75
+ 'error': result.stderr,
76
+ 'percentage': 0
77
+ }
78
+ except Exception as e:
79
+ return {
80
+ 'error': str(e),
81
+ 'percentage': 0
82
+ }
83
+
84
+ def check_watcher_status() -> Dict[str, Any]:
85
+ """Check if Docker watcher is running"""
86
+ try:
87
+ import subprocess
88
+ result = subprocess.run(
89
+ ["docker", "ps", "--filter", "name=claude-reflection-safe-watcher", "--format", "{{.Status}}"],
90
+ capture_output=True,
91
+ text=True,
92
+ timeout=5
93
+ )
94
+
95
+ if result.stdout and "Up" in result.stdout:
96
+ return {
97
+ 'status': 'running',
98
+ 'details': result.stdout.strip()
99
+ }
100
+ else:
101
+ return {
102
+ 'status': 'stopped',
103
+ 'details': 'Container not running'
104
+ }
105
+ except Exception as e:
106
+ return {
107
+ 'status': 'unknown',
108
+ 'error': 'Docker check failed'
109
+ }
110
+
111
+ def check_recent_imports() -> Dict[str, Any]:
112
+ """Check for recent import activity"""
113
+ try:
114
+ import_file = Path.home() / ".claude-self-reflect" / "config" / "imported-files.json"
115
+ if import_file.exists():
116
+ mtime = datetime.fromtimestamp(import_file.stat().st_mtime)
117
+ age = datetime.now() - mtime
118
+
119
+ return {
120
+ 'last_import': mtime.isoformat(),
121
+ 'hours_ago': round(age.total_seconds() / 3600, 1),
122
+ 'active': age < timedelta(hours=24)
123
+ }
124
+ return {
125
+ 'last_import': None,
126
+ 'active': False
127
+ }
128
+ except Exception as e:
129
+ return {
130
+ 'error': 'Import check failed',
131
+ 'active': False
132
+ }
133
+
134
+ def get_health_status() -> Dict[str, Any]:
135
+ """Get comprehensive health status"""
136
+
137
+ # Collect all health checks
138
+ qdrant = check_qdrant_health()
139
+ imports = check_import_status()
140
+ watcher = check_watcher_status()
141
+ recent = check_recent_imports()
142
+
143
+ # Determine overall health
144
+ is_healthy = (
145
+ qdrant.get('accessible', False) and
146
+ imports.get('percentage', 0) > 95 and
147
+ watcher.get('status') == 'running'
148
+ )
149
+
150
+ return {
151
+ 'timestamp': datetime.now().isoformat(),
152
+ 'healthy': is_healthy,
153
+ 'status': 'healthy' if is_healthy else 'degraded',
154
+ 'components': {
155
+ 'qdrant': qdrant,
156
+ 'imports': imports,
157
+ 'watcher': watcher,
158
+ 'recent_activity': recent
159
+ },
160
+ 'summary': {
161
+ 'import_percentage': imports.get('percentage', 0),
162
+ 'collections': qdrant.get('collections', 0),
163
+ 'watcher_running': watcher.get('status') == 'running',
164
+ 'recent_imports': recent.get('active', False)
165
+ }
166
+ }
167
+
168
+ def main():
169
+ """Main entry point for CLI usage"""
170
+ try:
171
+ health = get_health_status()
172
+
173
+ # Pretty print
174
+ print(json.dumps(health, indent=2))
175
+
176
+ # Exit code based on health
177
+ sys.exit(0 if health['healthy'] else 1)
178
+
179
+ except Exception as e:
180
+ error_response = {
181
+ 'timestamp': datetime.now().isoformat(),
182
+ 'healthy': False,
183
+ 'status': 'error',
184
+ 'error': 'Health check failed'
185
+ }
186
+ print(json.dumps(error_response, indent=2))
187
+ sys.exit(2)
188
+
189
+ if __name__ == "__main__":
190
+ main()
@@ -76,31 +76,64 @@ class ProjectResolver:
76
76
  return []
77
77
 
78
78
  # Strategy 1: Direct hash of input (handles full paths)
79
- direct_hash = hashlib.sha256(user_project_name.encode()).hexdigest()[:16]
79
+ # Try both MD5 (used by streaming-watcher) and SHA256 (legacy)
80
+ direct_hash_md5 = hashlib.md5(user_project_name.encode()).hexdigest()[:8]
81
+ direct_hash_sha256 = hashlib.sha256(user_project_name.encode()).hexdigest()[:16]
82
+
80
83
  # Match exact hash segment between underscores, not substring
81
84
  direct_matches = [c for c in collection_names
82
- if f"_{direct_hash}_" in c or c.endswith(f"_{direct_hash}")]
85
+ if f"_{direct_hash_md5}_" in c or c.endswith(f"_{direct_hash_md5}") or
86
+ f"_{direct_hash_sha256}_" in c or c.endswith(f"_{direct_hash_sha256}")]
83
87
  matching_collections.update(direct_matches)
84
88
 
85
89
  # Strategy 2: Try normalized version
86
90
  normalized = self._normalize_project_name(user_project_name)
87
91
  if normalized != user_project_name:
88
- norm_hash = hashlib.sha256(normalized.encode()).hexdigest()[:16]
92
+ norm_hash_md5 = hashlib.md5(normalized.encode()).hexdigest()[:8]
93
+ norm_hash_sha256 = hashlib.sha256(normalized.encode()).hexdigest()[:16]
94
+
89
95
  # Match exact hash segment between underscores, not substring
90
96
  norm_matches = [c for c in collection_names
91
- if f"_{norm_hash}_" in c or c.endswith(f"_{norm_hash}")]
97
+ if f"_{norm_hash_md5}_" in c or c.endswith(f"_{norm_hash_md5}") or
98
+ f"_{norm_hash_sha256}_" in c or c.endswith(f"_{norm_hash_sha256}")]
92
99
  matching_collections.update(norm_matches)
93
100
 
94
101
  # Strategy 3: Case-insensitive normalized version
95
102
  lower_normalized = normalized.lower()
96
103
  if lower_normalized != normalized:
97
- lower_hash = hashlib.sha256(lower_normalized.encode()).hexdigest()[:16]
104
+ lower_hash_md5 = hashlib.md5(lower_normalized.encode()).hexdigest()[:8]
105
+ lower_hash_sha256 = hashlib.sha256(lower_normalized.encode()).hexdigest()[:16]
106
+
98
107
  # Match exact hash segment between underscores, not substring
99
108
  lower_matches = [c for c in collection_names
100
- if f"_{lower_hash}_" in c or c.endswith(f"_{lower_hash}")]
109
+ if f"_{lower_hash_md5}_" in c or c.endswith(f"_{lower_hash_md5}") or
110
+ f"_{lower_hash_sha256}_" in c or c.endswith(f"_{lower_hash_sha256}")]
101
111
  matching_collections.update(lower_matches)
102
112
 
103
- # Strategy 4: Use segment-based discovery for complex paths
113
+ # Strategy 4: ALWAYS try mapping project name to full directory path in .claude/projects/
114
+ # This ensures we find all related collections, not just the first match
115
+ # This handles the case where streaming-watcher uses full path but MCP uses short name
116
+ if not user_project_name.startswith('-'):
117
+ # Check if there's a matching directory in .claude/projects/
118
+ projects_dir = Path.home() / ".claude" / "projects"
119
+ if projects_dir.exists():
120
+ for proj_dir in projects_dir.iterdir():
121
+ if proj_dir.is_dir():
122
+ # Check if the directory name contains the project name
123
+ # This handles both "claude-self-reflect" and "-Users-...-projects-claude-self-reflect"
124
+ if (proj_dir.name.endswith(f"-{user_project_name}") or
125
+ f"-{user_project_name}" in proj_dir.name or
126
+ proj_dir.name == user_project_name):
127
+ # Found a matching directory - hash its name
128
+ dir_name = proj_dir.name
129
+ dir_hash_md5 = hashlib.md5(dir_name.encode()).hexdigest()[:8]
130
+
131
+ # Find collections with this hash
132
+ dir_matches = [c for c in collection_names
133
+ if f"_{dir_hash_md5}_" in c or c.endswith(f"_{dir_hash_md5}")]
134
+ matching_collections.update(dir_matches)
135
+
136
+ # Strategy 5: Use segment-based discovery for complex paths
104
137
  if not matching_collections:
105
138
  # Extract segments from the input
106
139
  segments = self._extract_project_segments(user_project_name)
@@ -111,10 +144,13 @@ class ProjectResolver:
111
144
 
112
145
  # Try each candidate
113
146
  for candidate in candidates:
114
- candidate_hash = hashlib.sha256(candidate.encode()).hexdigest()[:16]
147
+ candidate_hash_md5 = hashlib.md5(candidate.encode()).hexdigest()[:8]
148
+ candidate_hash_sha256 = hashlib.sha256(candidate.encode()).hexdigest()[:16]
149
+
115
150
  # Match exact hash segment between underscores, not substring
116
151
  candidate_matches = [c for c in collection_names
117
- if f"_{candidate_hash}_" in c or c.endswith(f"_{candidate_hash}")]
152
+ if f"_{candidate_hash_md5}_" in c or c.endswith(f"_{candidate_hash_md5}") or
153
+ f"_{candidate_hash_sha256}_" in c or c.endswith(f"_{candidate_hash_sha256}")]
118
154
  matching_collections.update(candidate_matches)
119
155
 
120
156
  # Stop if we found matches
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-self-reflect",
3
- "version": "2.8.4",
3
+ "version": "2.8.6",
4
4
  "description": "Give Claude perfect memory of all your conversations - Installation wizard for Python MCP server",
5
5
  "keywords": [
6
6
  "claude",
@@ -30,13 +30,16 @@ logger = logging.getLogger(__name__)
30
30
 
31
31
  # Environment variables
32
32
  QDRANT_URL = os.getenv("QDRANT_URL", "http://localhost:6333")
33
- STATE_FILE = os.getenv("STATE_FILE", "/config/imported-files.json")
33
+ STATE_FILE = os.getenv("STATE_FILE", os.path.expanduser("~/.claude-self-reflect/config/imported-files.json"))
34
34
  PREFER_LOCAL_EMBEDDINGS = os.getenv("PREFER_LOCAL_EMBEDDINGS", "true").lower() == "true"
35
35
  VOYAGE_API_KEY = os.getenv("VOYAGE_KEY")
36
36
  MAX_CHUNK_SIZE = int(os.getenv("MAX_CHUNK_SIZE", "50")) # Messages per chunk
37
37
 
38
- # Initialize Qdrant client
39
- client = QdrantClient(url=QDRANT_URL)
38
+ # Initialize Qdrant client with timeout
39
+ client = QdrantClient(
40
+ url=QDRANT_URL,
41
+ timeout=30 # 30 second timeout for network operations
42
+ )
40
43
 
41
44
  # Initialize embedding provider
42
45
  embedding_provider = None
@@ -138,11 +141,11 @@ def process_and_upload_chunk(messages: List[Dict[str, Any]], chunk_index: int,
138
141
  payload=payload
139
142
  )
140
143
 
141
- # Upload immediately
144
+ # Upload immediately (no wait for better throughput)
142
145
  client.upsert(
143
146
  collection_name=collection_name,
144
147
  points=[point],
145
- wait=True
148
+ wait=False # Don't wait for indexing to complete
146
149
  )
147
150
 
148
151
  return 1