claude-self-reflect 2.4.12 → 2.4.14

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.
@@ -5,12 +5,20 @@ WORKDIR /app
5
5
  # Update system packages for security
6
6
  RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/*
7
7
 
8
- # Install dependencies
9
- COPY scripts/requirements.txt .
10
- RUN pip install --no-cache-dir -r requirements.txt
8
+ # Install dependencies directly (avoids file path issues with global npm installs)
9
+ RUN pip install --no-cache-dir \
10
+ qdrant-client==1.15.0 \
11
+ openai==1.97.1 \
12
+ mcp-server-qdrant==0.8.0 \
13
+ backoff==2.2.1 \
14
+ tqdm==4.67.1 \
15
+ humanize==4.12.3 \
16
+ fastembed==0.7.1 \
17
+ voyageai==0.3.4 \
18
+ tenacity==9.1.2
11
19
 
12
- # Copy only the unified import script
13
- COPY scripts/import-conversations-unified.py .
20
+ # Note: The import script is mounted as a volume in docker-compose.yaml
21
+ # This allows the container to work with both local development and global npm installs
14
22
 
15
- # Run the unified importer
16
- CMD ["python", "import-conversations-unified.py"]
23
+ # Default command (can be overridden by docker-compose)
24
+ CMD ["python", "--version"]
package/README.md CHANGED
@@ -372,4 +372,19 @@ Special thanks to our contributors and security researchers:
372
372
  - **[@akamalov](https://github.com/akamalov)** - Highlighted Ubuntu WSL bug and helped educate about filesystem nuances
373
373
  - **[@kylesnowschwartz](https://github.com/kylesnowschwartz)** - Comprehensive security review leading to v2.3.3+ security improvements (#6)
374
374
 
375
+ ## Windows Configuration
376
+
377
+ ### Recommended: Use WSL
378
+ For the best experience on Windows, we recommend using WSL (Windows Subsystem for Linux) which provides native Linux compatibility for Docker operations.
379
+
380
+ ### Alternative: Native Windows
381
+ If using Docker Desktop on native Windows, you need to adjust the CONFIG_PATH in your `.env` file to use Docker-compatible paths:
382
+
383
+ ```bash
384
+ # Replace USERNAME with your Windows username
385
+ CONFIG_PATH=/c/Users/USERNAME/.claude-self-reflect/config
386
+ ```
387
+
388
+ This ensures Docker can properly mount the config directory. The setup wizard creates the directory, but Windows users need to update the path format for Docker compatibility.
389
+
375
390
  MIT License. Built with ❤️ for the Claude community.
@@ -7,7 +7,7 @@ services:
7
7
  image: alpine
8
8
  command: chown -R 1000:1000 /config
9
9
  volumes:
10
- - ./config:/config
10
+ - ${CONFIG_PATH:-~/.claude-self-reflect/config}:/config
11
11
  profiles: ["watch", "mcp", "import"]
12
12
 
13
13
  # Qdrant vector database - the heart of semantic search
@@ -36,7 +36,7 @@ services:
36
36
  - qdrant
37
37
  volumes:
38
38
  - ${CLAUDE_LOGS_PATH:-~/.claude/projects}:/logs:ro
39
- - ./config:/config
39
+ - ${CONFIG_PATH:-~/.claude-self-reflect/config}:/config
40
40
  - ./scripts:/scripts:ro
41
41
  environment:
42
42
  - QDRANT_URL=http://qdrant:6333
@@ -64,7 +64,7 @@ services:
64
64
  - qdrant
65
65
  volumes:
66
66
  - ${CLAUDE_LOGS_PATH:-~/.claude/projects}:/logs:ro
67
- - ./config:/config
67
+ - ${CONFIG_PATH:-~/.claude-self-reflect/config}:/config
68
68
  - ./scripts:/scripts:ro
69
69
  environment:
70
70
  - QDRANT_URL=http://qdrant:6333
@@ -7,6 +7,7 @@ import fs from 'fs/promises';
7
7
  import fsSync from 'fs';
8
8
  import readline from 'readline';
9
9
  import path from 'path';
10
+ import os from 'os';
10
11
 
11
12
  const __filename = fileURLToPath(import.meta.url);
12
13
  const __dirname = dirname(__filename);
@@ -94,6 +95,40 @@ async function checkDocker() {
94
95
  async function configureEnvironment() {
95
96
  console.log('\n🔐 Configuring environment...');
96
97
 
98
+ // Setup config directory in user's home directory for global npm installs
99
+ const userConfigDir = join(os.homedir(), '.claude-self-reflect', 'config');
100
+
101
+ try {
102
+ await fs.mkdir(userConfigDir, { recursive: true });
103
+ console.log(`📁 Using config directory: ${userConfigDir}`);
104
+
105
+ // Migrate existing config from project directory if it exists
106
+ const oldConfigDir = join(projectRoot, 'config');
107
+ try {
108
+ await fs.access(oldConfigDir);
109
+ const files = await fs.readdir(oldConfigDir);
110
+ if (files.length > 0) {
111
+ console.log('🔄 Migrating existing config data...');
112
+ for (const file of files) {
113
+ const sourcePath = join(oldConfigDir, file);
114
+ const targetPath = join(userConfigDir, file);
115
+ try {
116
+ await fs.copyFile(sourcePath, targetPath);
117
+ } catch (err) {
118
+ // Ignore copy errors, file might already exist
119
+ }
120
+ }
121
+ console.log('✅ Config migration completed');
122
+ }
123
+ } catch {
124
+ // No old config directory, nothing to migrate
125
+ }
126
+ } catch (error) {
127
+ console.log(`❌ Could not create config directory: ${error.message}`);
128
+ console.log(' This may cause Docker mount issues. Please check permissions.');
129
+ throw error;
130
+ }
131
+
97
132
  const envPath = join(projectRoot, '.env');
98
133
  let envContent = '';
99
134
  let hasValidApiKey = false;
@@ -153,6 +188,9 @@ async function configureEnvironment() {
153
188
  if (!envContent.includes('PREFER_LOCAL_EMBEDDINGS=')) {
154
189
  envContent += `PREFER_LOCAL_EMBEDDINGS=${localMode ? 'true' : 'false'}\n`;
155
190
  }
191
+ if (!envContent.includes('CONFIG_PATH=')) {
192
+ envContent += `CONFIG_PATH=${userConfigDir}\n`;
193
+ }
156
194
 
157
195
  await fs.writeFile(envPath, envContent.trim() + '\n');
158
196
  console.log('✅ Environment configured');
@@ -11,6 +11,7 @@ import hashlib
11
11
  import time
12
12
 
13
13
  from fastmcp import FastMCP, Context
14
+ from .utils import normalize_project_name
14
15
  from pydantic import BaseModel, Field
15
16
  from qdrant_client import AsyncQdrantClient, models
16
17
  from qdrant_client.models import (
@@ -233,8 +234,9 @@ async def reflect_on_past(
233
234
  # Filter collections by project if not searching all
234
235
  project_collections = [] # Define at this scope for later use
235
236
  if target_project != 'all':
236
- # Generate the collection name pattern for this project
237
- project_hash = hashlib.md5(target_project.encode()).hexdigest()[:8]
237
+ # Generate the collection name pattern for this project using normalized name
238
+ normalized_name = normalize_project_name(target_project)
239
+ project_hash = hashlib.md5(normalized_name.encode()).hexdigest()[:8]
238
240
  project_collections = [
239
241
  c for c in all_collections
240
242
  if c.startswith(f"conv_{project_hash}_")
@@ -0,0 +1,53 @@
1
+ """Shared utilities for claude-self-reflect MCP server and scripts."""
2
+
3
+ from pathlib import Path
4
+
5
+
6
+ def normalize_project_name(project_path: str) -> str:
7
+ """
8
+ Normalize project name for consistent hashing across import/search.
9
+
10
+ Handles various path formats:
11
+ - Claude logs format: -Users-kyle-Code-claude-self-reflect -> claude-self-reflect
12
+ - Regular paths: /path/to/project -> project
13
+ - Already normalized: project -> project
14
+
15
+ Args:
16
+ project_path: Project path or name in any format
17
+
18
+ Returns:
19
+ Normalized project name suitable for consistent hashing
20
+ """
21
+ if not project_path:
22
+ return ""
23
+
24
+ # Remove trailing slashes
25
+ project_path = project_path.rstrip('/')
26
+
27
+ # Handle Claude logs format (starts with dash)
28
+ if project_path.startswith('-'):
29
+ # For paths like -Users-kyle-Code-claude-self-reflect
30
+ # We want to extract the actual project name which may contain dashes
31
+ # Strategy: Find common parent directories and extract what comes after
32
+
33
+ # Remove leading dash and convert back to path-like format
34
+ path_str = project_path[1:].replace('-', '/')
35
+ path_parts = Path(path_str).parts
36
+
37
+ # Look for common project parent directories
38
+ project_parents = {'projects', 'code', 'Code', 'repos', 'repositories',
39
+ 'dev', 'Development', 'work', 'src', 'github'}
40
+
41
+ # Find the project name after a known parent directory
42
+ for i, part in enumerate(path_parts):
43
+ if part.lower() in project_parents and i + 1 < len(path_parts):
44
+ # Everything after the parent directory is the project name
45
+ # Join remaining parts with dash if project name has multiple components
46
+ remaining = path_parts[i + 1:]
47
+ return '-'.join(remaining)
48
+
49
+ # Fallback: just use the last component
50
+ return path_parts[-1] if path_parts else project_path
51
+
52
+ # Handle regular paths - use basename
53
+ return Path(project_path).name
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-self-reflect",
3
- "version": "2.4.12",
3
+ "version": "2.4.14",
4
4
  "description": "Give Claude perfect memory of all your conversations - Installation wizard for Python MCP server",
5
5
  "keywords": [
6
6
  "claude",
@@ -14,6 +14,10 @@ from typing import List, Dict, Any
14
14
  import logging
15
15
  from pathlib import Path
16
16
 
17
+ # Add the mcp-server/src directory to the Python path
18
+ sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'mcp-server', 'src'))
19
+ from utils import normalize_project_name
20
+
17
21
  from qdrant_client import QdrantClient
18
22
  from qdrant_client.models import (
19
23
  VectorParams, Distance, PointStruct,
@@ -343,10 +347,11 @@ def main():
343
347
  # Import each project
344
348
  total_imported = 0
345
349
  for project_dir in project_dirs:
346
- # Create collection name from project path
347
- collection_name = f"conv_{hashlib.md5(project_dir.name.encode()).hexdigest()[:8]}{collection_suffix}"
350
+ # Create collection name from normalized project name
351
+ normalized_name = normalize_project_name(project_dir.name)
352
+ collection_name = f"conv_{hashlib.md5(normalized_name.encode()).hexdigest()[:8]}{collection_suffix}"
348
353
 
349
- logger.info(f"Importing project: {project_dir.name} -> {collection_name}")
354
+ logger.info(f"Importing project: {project_dir.name} (normalized: {normalized_name}) -> {collection_name}")
350
355
  chunks = import_project(project_dir, collection_name, state)
351
356
  total_imported += chunks
352
357
  logger.info(f"Imported {chunks} chunks from {project_dir.name}")