claude-self-reflect 4.0.0 → 4.0.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.
@@ -0,0 +1,151 @@
1
+ ---
2
+ name: csr-validator
3
+ description: Validates Claude Self-Reflect system functionality. Use for testing MCP tools, embedding modes, import pipeline, and search. MUST BE USED before releases and after major changes.
4
+ tools: mcp__claude-self-reflect__switch_embedding_mode, mcp__claude-self-reflect__get_embedding_mode, mcp__claude-self-reflect__store_reflection, mcp__claude-self-reflect__csr_reflect_on_past, mcp__claude-self-reflect__csr_quick_check, mcp__claude-self-reflect__csr_search_insights, mcp__claude-self-reflect__get_recent_work, mcp__claude-self-reflect__search_by_recency, mcp__claude-self-reflect__get_timeline, mcp__claude-self-reflect__search_by_file, mcp__claude-self-reflect__search_by_concept, mcp__claude-self-reflect__get_full_conversation, mcp__claude-self-reflect__get_next_results, mcp__claude-self-reflect__csr_get_more, mcp__claude-self-reflect__reload_code, mcp__claude-self-reflect__reload_status, mcp__claude-self-reflect__clear_module_cache, Bash, Read
5
+ model: inherit
6
+ ---
7
+
8
+ You are a focused CSR system validator. Test ONLY through MCP protocol - NEVER import Python modules directly.
9
+
10
+ ## Test Sequence (MANDATORY ORDER)
11
+
12
+ ### 1. Mode Testing
13
+ ```
14
+ 1. Get current mode (get_embedding_mode)
15
+ 2. Switch to CLOUD mode (switch_embedding_mode)
16
+ 3. Verify 1024 dimensions
17
+ 4. Store test reflection with tag "cloud-test-{timestamp}"
18
+ 5. Search for it immediately
19
+ 6. Switch to LOCAL mode
20
+ 7. Verify 384 dimensions
21
+ 8. Store test reflection with tag "local-test-{timestamp}"
22
+ 9. Search for it immediately
23
+ ```
24
+
25
+ ### 2. MCP Tools Validation (ALL 15+)
26
+ Test each tool with minimal viable input:
27
+ - `csr_reflect_on_past`: Query "test"
28
+ - `csr_quick_check`: Query "system"
29
+ - `store_reflection`: Content with unique timestamp
30
+ - `get_recent_work`: Limit 2
31
+ - `search_by_recency`: Query "import", time_range "today"
32
+ - `get_timeline`: Range "last hour"
33
+ - `search_by_file`: Path "*.py"
34
+ - `search_by_concept`: Concept "testing"
35
+ - `get_full_conversation`: Use any recent ID
36
+ - `csr_search_insights`: Query "performance"
37
+ - `csr_get_more`: After any search
38
+ - `get_next_results`: After any search
39
+ - `reload_status`: Check reload state
40
+ - `clear_module_cache`: If needed
41
+ - `reload_code`: If status shows changes
42
+
43
+ ### 3. Security Scan (CRITICAL)
44
+ ```bash
45
+ # Scan for hardcoded paths
46
+ grep -r "/Users/[a-zA-Z]*/\|/home/[a-zA-Z]*/" scripts/ --include="*.py" | grep -v "^#" | head -20
47
+
48
+ # Scan for API keys/secrets (VOYAGE_KEY, etc)
49
+ grep -r "VOYAGE_KEY\|API_KEY\|SECRET\|PASSWORD" scripts/ --include="*.py" | grep -v "os.environ\|getenv" | head -10
50
+
51
+ # Check for sensitive patterns in state files
52
+ grep -E "(api_key|secret|password|token)" ~/.claude-self-reflect/config/*.json | head -10
53
+
54
+ # Find transient test files
55
+ find . -name "*test*.py" -o -name "*benchmark*.py" -o -name "*tmp*" -o -name "*.pyc" | grep -v ".git" | head -20
56
+ ```
57
+
58
+ ### 4. Performance Check
59
+ ```bash
60
+ # Via Bash tool only
61
+ time python -c "from datetime import datetime; print(datetime.now())"
62
+ ps aux | grep python | head -5
63
+ docker ps --format "table {{.Names}}\t{{.Status}}" | grep qdrant
64
+ ```
65
+
66
+ ### 5. State Verification
67
+ ```bash
68
+ # Check unified state
69
+ ls -la ~/.claude-self-reflect/config/unified-state.json
70
+ wc -l ~/.claude-self-reflect/config/unified-state.json
71
+ head -20 ~/.claude-self-reflect/config/unified-state.json
72
+ ```
73
+
74
+ ### 6. CodeRabbit CLI Analysis
75
+ ```bash
76
+ # Run CodeRabbit for code quality check
77
+ echo "=== Running CodeRabbit CLI ==="
78
+ coderabbit --version
79
+ script -q /dev/null coderabbit --prompt-only || echo "CodeRabbit CLI issues detected - terminal mode incompatibility"
80
+
81
+ # Alternative: Check GitHub PR for CodeRabbit comments
82
+ echo "=== Checking PR CodeRabbit feedback ==="
83
+ gh pr list --state open --limit 1 --json number --jq '.[0].number' | xargs -I {} gh pr view {} --comments | grep -A 5 "coderabbitai" || echo "No open PRs with CodeRabbit feedback"
84
+ ```
85
+
86
+ ### 7. Cleanup Transient Files
87
+ ```bash
88
+ # List transient files (DO NOT DELETE YET)
89
+ echo "=== Transient files found ==="
90
+ find . -type f \( -name "*test_*.py" -o -name "test_*.py" -o -name "*benchmark*.py" \) -not -path "./.git/*" -not -path "./tests/*"
91
+
92
+ # Archive or mark for deletion
93
+ echo "=== Suggest archiving to: tests/throwaway/ ==="
94
+ ```
95
+
96
+ ## Output Format
97
+
98
+ ```
99
+ CSR VALIDATION REPORT
100
+ ====================
101
+ SECURITY SCAN: [PASS/FAIL]
102
+ - Hardcoded paths: [0 found/X found - LIST THEM]
103
+ - API keys exposed: [0 found/X found - LIST THEM]
104
+ - Sensitive data: [none/FOUND - LIST]
105
+ - Transient files: [X files - LIST FOR CLEANUP]
106
+
107
+ Mode Switching: [PASS/FAIL]
108
+ - Local→Cloud: [✓/✗]
109
+ - Cloud→Local: [✓/✗]
110
+ - Dimensions: [384/1024 verified]
111
+
112
+ MCP Tools (15/15):
113
+ - csr_reflect_on_past: [✓/✗]
114
+ - [... list all ...]
115
+
116
+ Performance:
117
+ - Search latency: [Xms]
118
+ - Memory usage: [XMB]
119
+ - Qdrant status: [healthy/unhealthy]
120
+
121
+ CodeRabbit Analysis: [PASS/FAIL]
122
+ - CLI execution: [✓/✗ - terminal mode issues]
123
+ - PR feedback checked: [✓/✗]
124
+ - Issues found: [none/list]
125
+
126
+ Critical Issues: [none/list]
127
+
128
+ CLEANUP NEEDED:
129
+ - [ ] Remove: [list transient files]
130
+ - [ ] Archive: [list test files]
131
+ - [ ] Fix: [list hardcoded paths]
132
+
133
+ VERDICT: [GREEN/YELLOW/RED]
134
+ ```
135
+
136
+ ## Rules
137
+ 1. NEVER import Python modules (no `from X import Y`)
138
+ 2. Use ONLY mcp__claude-self-reflect__ prefixed tools
139
+ 3. Use Bash for system checks ONLY (no Python scripts)
140
+ 4. Report EVERY failure, even minor
141
+ 5. Test BOTH modes completely
142
+ 6. Restore to LOCAL mode at end
143
+ 7. Complete in <2 minutes
144
+
145
+ ## Failure Handling
146
+ - If any MCP tool fails: Report exact error, continue testing others
147
+ - If mode switch fails: CRITICAL - stop and report
148
+ - If search returns no results: Note but continue
149
+ - If Bash fails: Try alternative command
150
+
151
+ Focus: Validate MCP protocol layer functionality, not implementation details.
@@ -6,8 +6,42 @@ tools: Read, Write, Edit, Bash, Grep, Glob, LS, WebFetch
6
6
 
7
7
  You are an open-source project maintainer for the Claude Self Reflect project. Your expertise covers community management, release processes, and maintaining a healthy, welcoming project.
8
8
 
9
+ ## CRITICAL WORKFLOW - MUST FOLLOW THIS SEQUENCE
10
+
11
+ ### Complete Release Flow (CSR Tester → Open Source Maintainer → NPM)
12
+ 1. **Code Review Phase**
13
+ - Check CodeRabbit feedback on existing PRs
14
+ - Fix ALL identified issues locally
15
+ - Create feature branch for fixes
16
+
17
+ 2. **PR Creation Phase**
18
+ - Create PR with all fixes
19
+ - Monitor CodeRabbit automated review on the PR
20
+ - Address any new issues CodeRabbit identifies
21
+ - Ensure all CI/CD checks pass
22
+
23
+ 3. **PR Merge Phase**
24
+ - Request review/approval
25
+ - Merge PR to main branch
26
+ - Verify merge completed successfully
27
+
28
+ 4. **Release Creation Phase**
29
+ - Create GitHub release with comprehensive notes
30
+ - Tag appropriately following semver
31
+ - Monitor automated workflows
32
+
33
+ 5. **NPM Publication Phase**
34
+ - Watch CI/CD pipeline for npm publish
35
+ - Verify package published to npm registry
36
+ - Test installation: `npm install -g claude-self-reflect@latest`
37
+
38
+ 6. **Post-Release Phase**
39
+ - Close related issues with release references
40
+ - Update project documentation
41
+ - Announce release in discussions/social
42
+
9
43
  ## Core Workflow: Explore, Plan, Execute, Verify
10
- 1. **Explore**: Read relevant files, check git history, review PRs
44
+ 1. **Explore**: Read relevant files, check git history, review PRs, check CodeRabbit feedback
11
45
  2. **Plan**: Think hard about the release strategy before executing
12
46
  3. **Execute**: Implement the release with proper checks
13
47
  4. **Verify**: Use independent verification (or ask user to verify)
@@ -81,13 +115,18 @@ git log -p --grep="feature name"
81
115
  gh pr list --state merged --limit 10
82
116
  ```
83
117
 
84
- ### PR Review Process
118
+ ### PR Review Process with CodeRabbit
85
119
  1. Thank contributor for their time
86
- 2. Run CI/CD checks
87
- 3. Review code for quality and style
88
- 4. Test changes locally
89
- 5. Provide constructive feedback
90
- 6. Merge with descriptive commit message
120
+ 2. Check CodeRabbit automated review comments
121
+ ```bash
122
+ gh pr view PR_NUMBER --comments | grep -B2 -A10 "coderabbitai"
123
+ ```
124
+ 3. Address any CodeRabbit-identified issues
125
+ 4. Run CI/CD checks
126
+ 5. Review code for quality and style
127
+ 6. Test changes locally
128
+ 7. Provide constructive feedback
129
+ 8. Merge with descriptive commit message
91
130
 
92
131
  ### Release Checklist
93
132
 
@@ -83,9 +83,14 @@ async def search_single_collection(
83
83
  with_payload=True
84
84
  )
85
85
 
86
+ # CRITICAL FIX: Handle None search results (cloud mode issue)
87
+ if search_results is None:
88
+ logger.warning(f"Search returned None for collection {collection_name}")
89
+ search_results = []
90
+
86
91
  # Debug: Log search results
87
92
  logger.debug(f"Search of {collection_name} returned {len(search_results)} results")
88
-
93
+
89
94
  if should_use_decay and not USE_NATIVE_DECAY:
90
95
  # Apply client-side decay
91
96
  await ctx.debug(f"Using CLIENT-SIDE decay for {collection_name}")
@@ -102,9 +102,15 @@ class SearchTools:
102
102
  collection_name=collection_name,
103
103
  query_vector=query_embedding,
104
104
  limit=limit,
105
- score_threshold=min_score
105
+ score_threshold=min_score,
106
+ with_payload=True # Explicitly request payloads from Qdrant
106
107
  )
107
-
108
+
109
+ # CRITICAL FIX: Handle None search results (cloud mode issue)
110
+ if search_results is None:
111
+ logger.warning(f"Search returned None for collection {collection_name}")
112
+ search_results = []
113
+
108
114
  # Convert results to dict format
109
115
  results = []
110
116
  for result in search_results:
@@ -0,0 +1,286 @@
1
+ """Ultra-fast status checker using unified state management.
2
+
3
+ This module reads from the unified state file for indexing status.
4
+ Designed for <20ms execution time to support status bars and shell scripts.
5
+ """
6
+
7
+ import json
8
+ import time
9
+ import sys
10
+ from pathlib import Path
11
+ from collections import defaultdict
12
+
13
+ # Add scripts directory to path for unified state manager
14
+ scripts_dir = Path(__file__).parent.parent.parent / "scripts"
15
+ if scripts_dir.exists():
16
+ sys.path.insert(0, str(scripts_dir))
17
+
18
+ try:
19
+ from unified_state_manager import UnifiedStateManager
20
+ except ImportError:
21
+ # Fallback to reading JSON directly if manager not available
22
+ UnifiedStateManager = None
23
+
24
+ # Try to import shared utilities
25
+ try:
26
+ from shared_utils import (
27
+ extract_project_name_from_path,
28
+ get_claude_projects_dir,
29
+ get_csr_config_dir
30
+ )
31
+ except ImportError:
32
+ # Fallback implementations
33
+ def extract_project_name_from_path(file_path: str) -> str:
34
+ """Extract project name from JSONL file path."""
35
+ path_obj = Path(file_path)
36
+ dir_name = path_obj.parent.name
37
+
38
+ if dir_name.startswith('-') and 'projects' in dir_name:
39
+ parts = dir_name.split('-')
40
+ try:
41
+ projects_idx = parts.index('projects')
42
+ if projects_idx + 1 < len(parts):
43
+ project_parts = parts[projects_idx + 1:]
44
+ return '-'.join(project_parts)
45
+ except ValueError:
46
+ pass
47
+
48
+ return dir_name.lstrip('-')
49
+
50
+ def get_claude_projects_dir() -> Path:
51
+ """Get Claude projects directory."""
52
+ import os
53
+ if 'CLAUDE_PROJECTS_DIR' in os.environ:
54
+ return Path(os.environ['CLAUDE_PROJECTS_DIR'])
55
+ return Path.home() / ".claude" / "projects"
56
+
57
+ def get_csr_config_dir() -> Path:
58
+ """Get CSR config directory."""
59
+ import os
60
+ if 'CSR_CONFIG_DIR' in os.environ:
61
+ return Path(os.environ['CSR_CONFIG_DIR'])
62
+ return Path.home() / '.claude-self-reflect' / 'config'
63
+
64
+
65
+ def get_watcher_status() -> dict:
66
+ """Get streaming watcher status from unified state."""
67
+ try:
68
+ if UnifiedStateManager:
69
+ manager = UnifiedStateManager()
70
+ state = manager.read_state()
71
+
72
+ # Get watcher status from importers section
73
+ watcher_info = state.get("importers", {}).get("streaming", {})
74
+ last_run = watcher_info.get("last_run")
75
+
76
+ if last_run:
77
+ from datetime import datetime, timezone
78
+ last_run_dt = datetime.fromisoformat(last_run)
79
+ now = datetime.now(timezone.utc)
80
+ age_seconds = (now - last_run_dt).total_seconds()
81
+ is_active = age_seconds < 120 # Active if updated in last 2 minutes
82
+ else:
83
+ is_active = False
84
+ age_seconds = float('inf')
85
+
86
+ return {
87
+ "running": is_active,
88
+ "files_processed": watcher_info.get("files_processed", 0),
89
+ "last_update_seconds": int(age_seconds) if age_seconds != float('inf') else None,
90
+ "status": "🟢 active" if is_active else "🔴 inactive"
91
+ }
92
+ else:
93
+ # Fallback to old method if UnifiedStateManager not available
94
+ watcher_state_file = get_csr_config_dir() / "csr-watcher.json"
95
+
96
+ if not watcher_state_file.exists():
97
+ return {"running": False, "status": "not configured"}
98
+
99
+ with open(watcher_state_file) as f:
100
+ state = json.load(f)
101
+
102
+ file_age = time.time() - watcher_state_file.stat().st_mtime
103
+ is_active = file_age < 120
104
+
105
+ return {
106
+ "running": is_active,
107
+ "files_processed": len(state.get("imported_files", {})),
108
+ "last_update_seconds": int(file_age),
109
+ "status": "🟢 active" if is_active else "🔴 inactive"
110
+ }
111
+ except Exception as e:
112
+ return {"running": False, "status": f"error: {str(e)[:50]}"}
113
+
114
+
115
+ def get_status() -> dict:
116
+ """Get indexing status from unified state with per-project breakdown.
117
+
118
+ Returns:
119
+ dict: JSON structure with overall and per-project indexing status
120
+ """
121
+ start_time = time.time()
122
+
123
+ try:
124
+ if UnifiedStateManager:
125
+ # Use unified state manager for fast access
126
+ manager = UnifiedStateManager()
127
+ status = manager.get_status()
128
+
129
+ # Get per-project breakdown
130
+ project_stats = defaultdict(lambda: {"indexed": 0, "total": 0})
131
+
132
+ # Count total JSONL files per project
133
+ projects_dir = get_claude_projects_dir()
134
+ if projects_dir.exists():
135
+ for jsonl_file in projects_dir.glob("**/*.jsonl"):
136
+ project_name = extract_project_name_from_path(str(jsonl_file))
137
+ project_stats[project_name]["total"] += 1
138
+
139
+ # Count indexed files per project from unified state
140
+ state = manager.read_state()
141
+ for file_path, metadata in state.get("files", {}).items():
142
+ if metadata.get("status") == "completed":
143
+ project_name = extract_project_name_from_path(file_path)
144
+ if project_name in project_stats:
145
+ project_stats[project_name]["indexed"] += 1
146
+
147
+ # Format response
148
+ result = {
149
+ "overall": {
150
+ "percentage": status["percentage"],
151
+ "indexed_files": status["indexed_files"],
152
+ "total_files": status["total_files"],
153
+ "total_chunks": status["total_chunks"],
154
+ },
155
+ "watcher": get_watcher_status(),
156
+ "projects": dict(project_stats),
157
+ "execution_time_ms": round((time.time() - start_time) * 1000, 2)
158
+ }
159
+
160
+ return result
161
+
162
+ else:
163
+ # Fallback to old multi-file method
164
+ return get_status_legacy()
165
+
166
+ except Exception as e:
167
+ return {
168
+ "error": str(e),
169
+ "execution_time_ms": round((time.time() - start_time) * 1000, 2)
170
+ }
171
+
172
+
173
+ def get_status_legacy() -> dict:
174
+ """Legacy status method reading from multiple files (fallback)."""
175
+ projects_dir = get_claude_projects_dir()
176
+ project_stats = defaultdict(lambda: {"indexed": 0, "total": 0})
177
+
178
+ # Count total JSONL files per project
179
+ if projects_dir.exists():
180
+ for jsonl_file in projects_dir.glob("**/*.jsonl"):
181
+ file_str = str(jsonl_file)
182
+ project_name = extract_project_name_from_path(file_str)
183
+ project_stats[project_name]["total"] += 1
184
+
185
+ # Read imported-files.json to count indexed files
186
+ config_dir = get_csr_config_dir()
187
+ imported_files_path = config_dir / "imported-files.json"
188
+
189
+ if imported_files_path.exists():
190
+ try:
191
+ with open(imported_files_path, 'r') as f:
192
+ data = json.load(f)
193
+ imported_files = data.get("imported_files", {})
194
+
195
+ for file_path in imported_files.keys():
196
+ # Normalize path
197
+ if file_path.startswith("/logs/"):
198
+ projects_path = str(get_claude_projects_dir())
199
+ normalized_path = file_path.replace("/logs/", projects_path + "/", 1)
200
+ else:
201
+ normalized_path = file_path
202
+
203
+ # Check if file exists and count it
204
+ if Path(normalized_path).exists():
205
+ project_name = extract_project_name_from_path(normalized_path)
206
+ if project_name in project_stats:
207
+ project_stats[project_name]["indexed"] += 1
208
+ except Exception:
209
+ pass
210
+
211
+ # Calculate overall stats
212
+ total_files = sum(p["total"] for p in project_stats.values())
213
+ indexed_files = sum(p["indexed"] for p in project_stats.values())
214
+ percentage = (indexed_files / max(total_files, 1)) * 100
215
+
216
+ return {
217
+ "overall": {
218
+ "percentage": percentage,
219
+ "indexed_files": indexed_files,
220
+ "total_files": total_files
221
+ },
222
+ "watcher": get_watcher_status(),
223
+ "projects": dict(project_stats)
224
+ }
225
+
226
+
227
+ def main():
228
+ """CLI interface for status checking."""
229
+ import argparse
230
+
231
+ parser = argparse.ArgumentParser(description="Check Claude Self-Reflect indexing status")
232
+ parser.add_argument("--format", choices=["json", "text"], default="json",
233
+ help="Output format (default: json)")
234
+ parser.add_argument("--watch", action="store_true",
235
+ help="Watch mode - update every 2 seconds")
236
+
237
+ args = parser.parse_args()
238
+
239
+ if args.watch:
240
+ try:
241
+ while True:
242
+ status = get_status()
243
+ if args.format == "json":
244
+ print(json.dumps(status, indent=2))
245
+ else:
246
+ overall = status.get("overall", {})
247
+ print(f"Indexing: {overall.get('percentage', 0):.1f}% "
248
+ f"({overall.get('indexed_files', 0)}/{overall.get('total_files', 0)})")
249
+
250
+ watcher = status.get("watcher", {})
251
+ print(f"Watcher: {watcher.get('status', '🔴 inactive')}")
252
+
253
+ if status.get("execution_time_ms"):
254
+ print(f"Time: {status['execution_time_ms']}ms")
255
+
256
+ print("\n" + "-" * 40)
257
+ time.sleep(2)
258
+
259
+ except KeyboardInterrupt:
260
+ print("\nStopped")
261
+ else:
262
+ status = get_status()
263
+ if args.format == "json":
264
+ print(json.dumps(status, indent=2))
265
+ else:
266
+ overall = status.get("overall", {})
267
+ print(f"Indexing: {overall.get('percentage', 0):.1f}% "
268
+ f"({overall.get('indexed_files', 0)}/{overall.get('total_files', 0)} files)")
269
+
270
+ watcher = status.get("watcher", {})
271
+ print(f"Watcher: {watcher.get('status', '🔴 inactive')}")
272
+
273
+ # Show per-project if available
274
+ projects = status.get("projects", {})
275
+ if projects:
276
+ print("\nProjects:")
277
+ for proj, stats in projects.items():
278
+ pct = (stats["indexed"] / max(stats["total"], 1)) * 100
279
+ print(f" {proj}: {pct:.1f}% ({stats['indexed']}/{stats['total']})")
280
+
281
+ if status.get("execution_time_ms"):
282
+ print(f"\nExecution time: {status['execution_time_ms']}ms")
283
+
284
+
285
+ if __name__ == "__main__":
286
+ main()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-self-reflect",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
4
4
  "description": "Give Claude perfect memory of all your conversations - Installation wizard for Python MCP server",
5
5
  "keywords": [
6
6
  "claude",
@@ -35,6 +35,9 @@
35
35
  },
36
36
  "files": [
37
37
  "installer/*.js",
38
+ "scripts/auto-migrate.cjs",
39
+ "scripts/migrate-to-unified-state.py",
40
+ "scripts/unified_state_manager.py",
38
41
  "scripts/csr-status",
39
42
  "scripts/session_quality_tracker.py",
40
43
  "scripts/ast_grep_final_analyzer.py",
@@ -68,7 +71,7 @@
68
71
  "LICENSE"
69
72
  ],
70
73
  "scripts": {
71
- "postinstall": "node installer/postinstall.js"
74
+ "postinstall": "node installer/postinstall.js && node scripts/auto-migrate.cjs || true"
72
75
  },
73
76
  "engines": {
74
77
  "node": ">=18.0.0"
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { execSync } = require('child_process');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const os = require('os');
7
+
8
+ console.log('🔄 Claude Self-Reflect: Checking for required migrations...');
9
+
10
+ const homeDir = os.homedir();
11
+ const csrConfigDir = path.join(homeDir, '.claude-self-reflect', 'config');
12
+ const unifiedStateFile = path.join(csrConfigDir, 'unified-state.json');
13
+ const legacyFiles = [
14
+ 'imported-files.json',
15
+ 'skipped_files.json',
16
+ 'failed_files.json',
17
+ 'import-status.json',
18
+ 'streaming-state.json'
19
+ ];
20
+
21
+ // Check if migration is needed
22
+ const needsMigration = legacyFiles.some(file =>
23
+ fs.existsSync(path.join(csrConfigDir, file))
24
+ );
25
+
26
+ if (!needsMigration && fs.existsSync(unifiedStateFile)) {
27
+ console.log('✅ Already using Unified State Management v5.0');
28
+ process.exit(0);
29
+ }
30
+
31
+ if (needsMigration) {
32
+ console.log('📦 Legacy state files detected. Running automatic migration...');
33
+ console.log('📋 Creating backup of existing state files...');
34
+
35
+ try {
36
+ // Check if Python is available
37
+ try {
38
+ execSync('python3 --version', { stdio: 'ignore' });
39
+ } catch {
40
+ console.log('⚠️ Python 3 not found. Migration will run when you first use the MCP server.');
41
+ console.log(' To run migration manually: python3 scripts/migrate-to-unified-state.py');
42
+ process.exit(0);
43
+ }
44
+
45
+ // Check if the migration script exists (npm global install location)
46
+ const scriptLocations = [
47
+ path.join(__dirname, 'migrate-to-unified-state.py'),
48
+ path.join(homeDir, '.claude-self-reflect', 'scripts', 'migrate-to-unified-state.py'),
49
+ path.join(process.cwd(), 'scripts', 'migrate-to-unified-state.py')
50
+ ];
51
+
52
+ let migrationScript = null;
53
+ for (const location of scriptLocations) {
54
+ if (fs.existsSync(location)) {
55
+ migrationScript = location;
56
+ break;
57
+ }
58
+ }
59
+
60
+ if (!migrationScript) {
61
+ console.log('⚠️ Migration script not found. It will run automatically when the MCP server starts.');
62
+ process.exit(0);
63
+ }
64
+
65
+ // Run the migration
66
+ console.log(`🚀 Running migration from: ${migrationScript}`);
67
+ const result = execSync(`python3 "${migrationScript}"`, {
68
+ encoding: 'utf-8',
69
+ stdio: 'pipe'
70
+ });
71
+
72
+ console.log(result);
73
+ console.log('✅ Migration completed successfully!');
74
+ console.log('🎉 Now using Unified State Management v5.0 (20x faster!)');
75
+
76
+ } catch (error) {
77
+ console.log('⚠️ Migration encountered an issue:', error.message);
78
+ console.log(' Your existing state files are preserved.');
79
+ console.log(' To run migration manually: python3 scripts/migrate-to-unified-state.py');
80
+ console.log(' For help: https://github.com/ramakay/claude-self-reflect/issues');
81
+ }
82
+ } else {
83
+ console.log('✅ Fresh installation - using Unified State Management v5.0');
84
+ }