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 +3 -0
- package/installer/setup-wizard-docker.js +5 -5
- package/mcp-server/pyproject.toml +1 -0
- package/mcp-server/requirements.txt +1 -0
- package/package.json +1 -1
- package/scripts/requirements.txt +3 -3
- package/src/runtime/batch_watcher.py +21 -15
- package/src/runtime/import-conversations-unified.py +3 -2
- package/src/runtime/unified_state_manager.py +16 -16
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
|
package/package.json
CHANGED
package/scripts/requirements.txt
CHANGED
|
@@ -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
|
-
|
|
295
|
-
import subprocess
|
|
294
|
+
import subprocess
|
|
296
295
|
|
|
297
|
-
|
|
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
|
|
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
|
|
327
|
-
logger.error("❌
|
|
319
|
+
except subprocess.TimeoutExpired as te:
|
|
320
|
+
logger.error("❌ Batch import timed out after %ss", SUBPROCESS_TIMEOUT_SECONDS)
|
|
328
321
|
|
|
329
|
-
# Re-queue
|
|
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.
|
|
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
|
|
386
|
+
for base in docker_allowed_bases:
|
|
385
387
|
try:
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
break
|
|
397
|
+
resolved.relative_to(base)
|
|
398
|
+
path_allowed = True
|
|
399
|
+
break
|
|
400
400
|
except ValueError:
|
|
401
401
|
continue
|
|
402
402
|
|