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.
- package/Dockerfile.importer +15 -7
- package/README.md +15 -0
- package/docker-compose.yaml +3 -3
- package/installer/setup-wizard-docker.js +38 -0
- package/mcp-server/src/server.py +4 -2
- package/mcp-server/src/utils.py +53 -0
- package/package.json +1 -1
- package/scripts/import-conversations-unified.py +8 -3
package/Dockerfile.importer
CHANGED
|
@@ -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
|
-
|
|
10
|
-
|
|
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
|
-
#
|
|
13
|
-
|
|
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
|
-
#
|
|
16
|
-
CMD ["python", "
|
|
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.
|
package/docker-compose.yaml
CHANGED
|
@@ -7,7 +7,7 @@ services:
|
|
|
7
7
|
image: alpine
|
|
8
8
|
command: chown -R 1000:1000 /config
|
|
9
9
|
volumes:
|
|
10
|
-
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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');
|
package/mcp-server/src/server.py
CHANGED
|
@@ -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
|
-
|
|
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
|
@@ -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
|
|
347
|
-
|
|
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}")
|