claude-self-reflect 2.4.8 → 2.4.9

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.
@@ -770,4 +770,48 @@ After importing:
770
770
  4. **Restart MCP After Import**: Ensures new collections are recognized
771
771
  5. **Verify with Search**: Test that new content is searchable
772
772
 
773
+ ## Quick Import for Current Project (NEW in v2.4.8)
774
+
775
+ For rapid updates when working on a single project, use the optimized quick import:
776
+
777
+ ### Quick Import Script
778
+ ```bash
779
+ # Import only recent conversations (last 2 hours by default)
780
+ cd /path/to/your/project
781
+ source ~/claude-self-reflect/venv/bin/activate
782
+ python ~/claude-self-reflect/scripts/import-latest.py
783
+
784
+ # Customize time window
785
+ export IMPORT_HOURS_BACK=4 # Import last 4 hours
786
+ python ~/claude-self-reflect/scripts/import-latest.py
787
+ ```
788
+
789
+ ### PreCompact Hook Integration
790
+ To automatically update conversations before compacting:
791
+
792
+ ```bash
793
+ # Install the hook (one-time setup)
794
+ cp ~/claude-self-reflect/scripts/precompact-hook.sh ~/.claude/hooks/precompact
795
+ # Or source it from your existing precompact hook:
796
+ echo "source ~/claude-self-reflect/scripts/precompact-hook.sh" >> ~/.claude/hooks/precompact
797
+ ```
798
+
799
+ ### Performance Expectations
800
+ - **Full import**: 2-7 minutes (all projects, all history)
801
+ - **Quick import**: 30-60 seconds (current project, recent files only)
802
+ - **Target**: <10 seconds (future optimization)
803
+
804
+ ### When to Use Quick Import
805
+ - Before starting a new Claude session
806
+ - After significant conversation progress
807
+ - Via PreCompact hook (automatic)
808
+ - When recent conversations aren't in search results
809
+
810
+ ### Troubleshooting Quick Import
811
+ If quick import fails:
812
+ 1. Ensure you're in a project directory with Claude logs
813
+ 2. Check virtual environment is activated
814
+ 3. Verify project has a collection: `python scripts/check-collections.py`
815
+ 4. For first-time projects, run full import once
816
+
773
817
  Remember: You're not just a search tool - you're a memory augmentation system that helps maintain continuity, prevent repeated work, and leverage collective knowledge across all Claude conversations.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-self-reflect",
3
- "version": "2.4.8",
3
+ "version": "2.4.9",
4
4
  "description": "Give Claude perfect memory of all your conversations - Installation wizard for Python MCP server",
5
5
  "keywords": [
6
6
  "claude",
@@ -37,6 +37,60 @@ VOYAGE_API_KEY = os.getenv("VOYAGE_KEY")
37
37
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
38
38
  logger = logging.getLogger(__name__)
39
39
 
40
+ # State management functions
41
+ def load_state():
42
+ """Load the import state from file."""
43
+ if os.path.exists(STATE_FILE):
44
+ try:
45
+ with open(STATE_FILE, 'r') as f:
46
+ state = json.load(f)
47
+ # Ensure the expected structure exists
48
+ if "imported_files" not in state:
49
+ state["imported_files"] = {}
50
+ return state
51
+ except Exception as e:
52
+ logger.warning(f"Failed to load state file: {e}")
53
+ return {"imported_files": {}}
54
+
55
+ def save_state(state):
56
+ """Save the import state to file."""
57
+ try:
58
+ # Ensure directory exists
59
+ os.makedirs(os.path.dirname(STATE_FILE), exist_ok=True)
60
+ # Write atomically by using a temp file
61
+ temp_file = STATE_FILE + ".tmp"
62
+ with open(temp_file, 'w') as f:
63
+ json.dump(state, f, indent=2)
64
+ os.replace(temp_file, STATE_FILE)
65
+ logger.debug(f"Saved state with {len(state['imported_files'])} files")
66
+ except Exception as e:
67
+ logger.error(f"Failed to save state file: {e}")
68
+
69
+ def should_import_file(file_path, state):
70
+ """Check if a file should be imported based on modification time."""
71
+ str_path = str(file_path)
72
+ file_mtime = os.path.getmtime(file_path)
73
+
74
+ if str_path in state["imported_files"]:
75
+ last_imported = state["imported_files"][str_path].get("last_imported", 0)
76
+ last_modified = state["imported_files"][str_path].get("last_modified", 0)
77
+
78
+ # Skip if file hasn't been modified since last import
79
+ if file_mtime <= last_modified and last_imported > 0:
80
+ logger.info(f"Skipping unchanged file: {file_path.name}")
81
+ return False
82
+
83
+ return True
84
+
85
+ def update_file_state(file_path, state, chunks_imported):
86
+ """Update the state for an imported file."""
87
+ str_path = str(file_path)
88
+ state["imported_files"][str_path] = {
89
+ "last_modified": os.path.getmtime(file_path),
90
+ "last_imported": datetime.now().timestamp(),
91
+ "chunks_imported": chunks_imported
92
+ }
93
+
40
94
  # Initialize embedding provider
41
95
  embedding_provider = None
42
96
  embedding_dimension = None
@@ -120,7 +174,7 @@ def chunk_conversation(messages: List[Dict[str, Any]], chunk_size: int = 10) ->
120
174
 
121
175
  return chunks
122
176
 
123
- def import_project(project_path: Path, collection_name: str) -> int:
177
+ def import_project(project_path: Path, collection_name: str, state: dict) -> int:
124
178
  """Import all conversations from a project."""
125
179
  jsonl_files = list(project_path.glob("*.jsonl"))
126
180
 
@@ -143,6 +197,10 @@ def import_project(project_path: Path, collection_name: str) -> int:
143
197
  total_chunks = 0
144
198
 
145
199
  for jsonl_file in jsonl_files:
200
+ # Check if file should be imported
201
+ if not should_import_file(jsonl_file, state):
202
+ continue
203
+
146
204
  logger.info(f"Processing file: {jsonl_file.name}")
147
205
  try:
148
206
  # Read JSONL file and extract messages
@@ -241,7 +299,11 @@ def import_project(project_path: Path, collection_name: str) -> int:
241
299
 
242
300
  total_chunks += len(points)
243
301
 
244
- logger.info(f"Imported {len(chunks)} chunks from {jsonl_file.name}")
302
+ file_chunks = len(chunks)
303
+ logger.info(f"Imported {file_chunks} chunks from {jsonl_file.name}")
304
+
305
+ # Update state for this file
306
+ update_file_state(jsonl_file, state, file_chunks)
245
307
 
246
308
  except Exception as e:
247
309
  logger.error(f"Failed to import {jsonl_file}: {e}")
@@ -258,6 +320,10 @@ def main():
258
320
  logger.error(f"Logs directory not found: {LOGS_DIR}")
259
321
  return
260
322
 
323
+ # Load existing state
324
+ state = load_state()
325
+ logger.info(f"Loaded state with {len(state['imported_files'])} previously imported files")
326
+
261
327
  # Find all project directories
262
328
  project_dirs = [d for d in logs_path.iterdir() if d.is_dir()]
263
329
 
@@ -274,9 +340,15 @@ def main():
274
340
  collection_name = f"conv_{hashlib.md5(project_dir.name.encode()).hexdigest()[:8]}{collection_suffix}"
275
341
 
276
342
  logger.info(f"Importing project: {project_dir.name} -> {collection_name}")
277
- chunks = import_project(project_dir, collection_name)
343
+ chunks = import_project(project_dir, collection_name, state)
278
344
  total_imported += chunks
279
345
  logger.info(f"Imported {chunks} chunks from {project_dir.name}")
346
+
347
+ # Save state after each project to avoid losing progress
348
+ save_state(state)
349
+
350
+ # Final save (redundant but ensures state is saved)
351
+ save_state(state)
280
352
 
281
353
  logger.info(f"Import complete! Total chunks imported: {total_imported}")
282
354