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.
- package/.claude/agents/csr-validator.md +151 -0
- package/.claude/agents/open-source-maintainer.md +46 -7
- package/mcp-server/src/parallel_search.py +6 -1
- package/mcp-server/src/search_tools.py +8 -2
- package/mcp-server/src/status_unified.py +286 -0
- package/package.json +5 -2
- package/scripts/auto-migrate.cjs +84 -0
- package/scripts/import-conversations-unified.py +96 -99
- package/scripts/migrate-to-unified-state.py +426 -0
- package/scripts/streaming-watcher.py +113 -158
- package/scripts/unified_state_manager.py +643 -0
|
@@ -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.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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.
|
|
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
|
+
}
|