claude-self-reflect 7.1.13 → 7.1.15

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/README.md CHANGED
@@ -205,6 +205,9 @@ Your code quality displayed live as you work:
205
205
 
206
206
  **9.3x Better Search Quality** • **50% Cost Savings** • **Fully Automated**
207
207
 
208
+ > [!IMPORTANT]
209
+ > **Opt-In Feature**: AI narratives require an Anthropic API key and are **enabled during CLI setup** when you answer "yes" to "Enable AI-powered narratives?". This sends conversation data to Anthropic for processing. Without an API key, CSR works normally with local-only search.
210
+
208
211
  v7.0 introduces AI-powered conversation narratives that transform raw conversation excerpts into rich problem-solution summaries with comprehensive metadata extraction.
209
212
 
210
213
  ### Before/After Comparison
@@ -537,7 +537,7 @@ async function enrichMetadata() {
537
537
  '-e', 'RATE_LIMIT_DELAY=0.5',
538
538
  '-e', 'MAX_CONCURRENT_UPDATES=2',
539
539
  'importer',
540
- 'python', '/app/scripts/delta-metadata-update-safe.py'
540
+ 'python', '/app/src/runtime/delta-metadata-update-safe.py'
541
541
  ], {
542
542
  cwd: projectRoot,
543
543
  stdio: 'inherit'
@@ -548,12 +548,12 @@ async function enrichMetadata() {
548
548
  } catch (error) {
549
549
  console.log('\n⚠️ Metadata enrichment had some issues but continuing setup');
550
550
  console.log(' You can retry later with:');
551
- console.log(' docker compose run --rm importer python /app/scripts/delta-metadata-update-safe.py');
551
+ console.log(' docker compose run --rm importer python /app/src/runtime/delta-metadata-update-safe.py');
552
552
  }
553
553
  } else {
554
554
  console.log('\n📝 Skipping metadata enrichment.');
555
555
  console.log(' You can run it later with:');
556
- console.log(' docker compose run --rm importer python /app/scripts/delta-metadata-update-safe.py');
556
+ console.log(' docker compose run --rm importer python /app/src/runtime/delta-metadata-update-safe.py');
557
557
  }
558
558
  }
559
559
 
@@ -770,7 +770,7 @@ async function showFinalInstructions() {
770
770
  console.log(' • Check status: docker compose ps');
771
771
  console.log(' • View logs: docker compose logs -f');
772
772
  console.log(' • Import conversations: docker compose run --rm importer');
773
- console.log(' • Enrich metadata: docker compose run --rm importer python /app/scripts/delta-metadata-update-safe.py');
773
+ console.log(' • Enrich metadata: docker compose run --rm importer python /app/src/runtime/delta-metadata-update-safe.py');
774
774
  console.log(' • Start watcher: docker compose --profile watch up -d');
775
775
  console.log(' • Ralph hooks: ./scripts/ralph/install_hooks.sh --check');
776
776
  console.log(' • Stop all: docker compose down');
@@ -817,7 +817,7 @@ async function checkExistingInstallation() {
817
817
  console.log('\n📋 Quick Commands:');
818
818
  console.log(' • View status: docker compose ps');
819
819
  console.log(' • View logs: docker compose logs -f');
820
- console.log(' • Enrich metadata: docker compose run --rm importer python /app/scripts/delta-metadata-update-safe.py');
820
+ console.log(' • Enrich metadata: docker compose run --rm importer python /app/src/runtime/delta-metadata-update-safe.py');
821
821
  console.log(' • Restart: docker compose restart');
822
822
  console.log(' • Stop: docker compose down');
823
823
 
@@ -16,6 +16,7 @@ dependencies = [
16
16
  "pydantic-settings>=2.0.0,<3.0.0",
17
17
  "fastembed>=0.4.0,<1.0.0",
18
18
  "dependency-injector>=4.0.0,<5.0.0",
19
+ "aiofiles>=23.0.0,<25.0.0",
19
20
  ]
20
21
 
21
22
  [project.scripts]
@@ -6,3 +6,4 @@ pydantic>=2.0.0
6
6
  pydantic-settings>=2.0.0
7
7
  numpy>=1.24.0
8
8
  fastembed>=0.2.0
9
+ aiofiles>=23.0.0,<25.0.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-self-reflect",
3
- "version": "7.1.13",
3
+ "version": "7.1.15",
4
4
  "description": "Give Claude perfect memory of all your conversations - Installation wizard for Python MCP server",
5
5
  "keywords": [
6
6
  "claude",
@@ -1,9 +1,9 @@
1
- qdrant-client==1.15.1
1
+ qdrant-client==1.16.0
2
2
  mcp-server-qdrant==0.8.0
3
3
  backoff==2.2.1
4
4
  tqdm==4.67.1
5
- humanize==4.13.0
5
+ humanize==4.14.0
6
6
  fastembed==0.7.3
7
7
  voyageai==0.3.5
8
8
  tenacity==9.1.2
9
- psutil==7.0.0
9
+ psutil==7.1.3
@@ -291,10 +291,10 @@ class BatchWatcher:
291
291
  logger.info(f" Files: {len(batch_files)}")
292
292
  logger.info(f"{'='*60}\n")
293
293
 
294
- try:
295
- import subprocess
294
+ import subprocess
296
295
 
297
- # Run batch import script with configurable timeout
296
+ # Step 1: Run the batch import
297
+ try:
298
298
  result = subprocess.run(
299
299
  [sys.executable, str(BATCH_IMPORT_SCRIPT)],
300
300
  capture_output=True,
@@ -306,29 +306,35 @@ class BatchWatcher:
306
306
  logger.info("\n✅ Batch triggered successfully")
307
307
  logger.info(" Output:\n%s", result.stdout)
308
308
 
309
- # Mark files as processed
310
- for entry in batch_files:
311
- self.state_manager.add_imported_file(
312
- file_path=entry["file_path"],
313
- chunks=0, # Will be updated by batch import
314
- metadata={"batch_queued": True}
315
- )
316
-
317
309
  except subprocess.CalledProcessError as cpe:
318
310
  logger.error("❌ Batch import failed (rc=%s)", cpe.returncode)
319
311
  logger.error(" Stdout: %s", cpe.stdout)
320
312
  logger.error(" Stderr: %s", cpe.stderr)
321
313
 
322
- # Re-queue failed files
314
+ # Re-queue only on actual batch failure
323
315
  for entry in batch_files:
324
316
  self.batch_queue.add(entry["file_path"], entry["project"])
317
+ return
325
318
 
326
- except Exception as e:
327
- logger.error("❌ Error triggering batch: %s", e, exc_info=True)
319
+ except subprocess.TimeoutExpired as te:
320
+ logger.error("❌ Batch import timed out after %ss", SUBPROCESS_TIMEOUT_SECONDS)
328
321
 
329
- # Re-queue failed files
322
+ # Re-queue on timeout
330
323
  for entry in batch_files:
331
324
  self.batch_queue.add(entry["file_path"], entry["project"])
325
+ return
326
+
327
+ # Step 2: Mark files as processed (batch succeeded, don't re-queue on failure here)
328
+ for entry in batch_files:
329
+ try:
330
+ self.state_manager.add_imported_file(
331
+ file_path=entry["file_path"],
332
+ chunks=0,
333
+ importer="batch",
334
+ status="completed"
335
+ )
336
+ except Exception as e:
337
+ logger.warning("⚠️ Failed to mark %s as processed: %s", entry["file_path"], e)
332
338
 
333
339
  def _hot_cycle(self):
334
340
  """Fast cycle to check for HOT files only."""
@@ -51,6 +51,7 @@ logger = logging.getLogger(__name__)
51
51
  # Constants
52
52
  QDRANT_URL = os.getenv("QDRANT_URL", "http://localhost:6333")
53
53
  MAX_CHUNK_SIZE = int(os.getenv("MAX_CHUNK_SIZE", "50"))
54
+ LOGS_DIR = os.getenv("LOGS_DIR", os.path.expanduser("~/.claude/projects"))
54
55
 
55
56
 
56
57
  class ConversationImporter:
@@ -327,8 +328,8 @@ def main():
327
328
  sys.exit(1)
328
329
  projects = [project_path]
329
330
  else:
330
- # Import all projects
331
- claude_dir = Path.home() / ".claude" / "projects"
331
+ # Import all projects (supports LOGS_DIR env var for Docker)
332
+ claude_dir = Path(LOGS_DIR).expanduser().resolve()
332
333
  if not claude_dir.exists():
333
334
  logger.error(f"Claude projects directory not found: {claude_dir}")
334
335
  sys.exit(1)
@@ -364,39 +364,39 @@ class UnifiedStateManager:
364
364
  break
365
365
 
366
366
  # Validate path is within allowed directories
367
+ # Base allowed paths for local environment
367
368
  allowed_bases = [
368
369
  Path.home() / ".claude",
369
370
  Path.home() / ".claude-self-reflect",
370
371
  ]
371
372
 
372
- # Add Docker paths if they exist
373
- for docker_path in docker_paths:
374
- docker_base = Path(docker_path)
375
- if docker_base.exists():
376
- allowed_bases.append(docker_base)
377
-
378
373
  # Check if path is within allowed directories
379
374
  path_allowed = False
380
375
 
381
376
  # In Docker: validate original path against Docker mounts
377
+ # Note: No existence check needed - relative_to() works mathematically on paths
378
+ # Docker paths only added to allowed_bases when actually running in Docker context
382
379
  if is_docker_path:
380
+ # Add Docker mount paths only when validating Docker paths (security: don't widen trust on local)
381
+ docker_allowed_bases = allowed_bases.copy()
382
+ for docker_path in docker_paths:
383
+ docker_allowed_bases.append(Path(docker_path))
384
+
383
385
  original_resolved = Path(original_path_str).resolve()
384
- for base in allowed_bases:
386
+ for base in docker_allowed_bases:
385
387
  try:
386
- if base.exists():
387
- original_resolved.relative_to(base)
388
- path_allowed = True
389
- break
388
+ original_resolved.relative_to(base)
389
+ path_allowed = True
390
+ break
390
391
  except ValueError:
391
392
  continue
392
393
  else:
393
- # Local environment: validate transformed path
394
+ # Local environment: validate transformed path (no Docker paths in allowed list)
394
395
  for base in allowed_bases:
395
396
  try:
396
- if base.exists():
397
- resolved.relative_to(base)
398
- path_allowed = True
399
- break
397
+ resolved.relative_to(base)
398
+ path_allowed = True
399
+ break
400
400
  except ValueError:
401
401
  continue
402
402