claude-self-reflect 6.0.5 → 7.1.8

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.
@@ -2,13 +2,16 @@ volumes:
2
2
  qdrant_data:
3
3
 
4
4
  services:
5
- # Fix permissions for config directory
5
+ # Fix permissions for config directory (UID 1001 matches appuser in Dockerfiles)
6
+ # Now runs by default since batch services are always-on
6
7
  init-permissions:
7
8
  image: alpine
8
- command: chown -R 1000:1000 /config
9
+ command: sh -c "mkdir -p /config /batch_queue /batch_state && chown -R 1001:1001 /config && chown -R 1001:1001 /batch_queue && chown -R 1001:1001 /batch_state"
9
10
  volumes:
10
- - ${CONFIG_PATH:-~/.claude-self-reflect/config}:/config
11
- profiles: ["watch", "import", "async", "safe-watch"]
11
+ - ${CONFIG_PATH:-${HOME}/.claude-self-reflect/config}:/config
12
+ - ${CSR_BATCH_QUEUE_DIR:-${HOME}/.claude-self-reflect/batch_queue}:/batch_queue
13
+ - ${CSR_BATCH_STATE_DIR:-${HOME}/.claude-self-reflect/batch_state}:/batch_state
14
+ # No profiles = starts by default (required for batch services)
12
15
 
13
16
  # Qdrant vector database - the heart of semantic search
14
17
  qdrant:
@@ -20,7 +23,7 @@ services:
20
23
  - qdrant_data:/qdrant/storage
21
24
  # Note: Using CONFIG_PATH variable to support global npm installs (fixes #71)
22
25
  # macOS Docker Desktop restricts mounts to /Users, /Volumes, /private, /tmp
23
- - ${CONFIG_PATH:-~/.claude-self-reflect/config}/qdrant-config.yaml:/qdrant/config/config.yaml:ro
26
+ - ${CONFIG_PATH:-${HOME}/.claude-self-reflect/config}/qdrant-config.yaml:/qdrant/config/config.yaml:ro
24
27
  environment:
25
28
  - QDRANT__LOG_LEVEL=INFO
26
29
  - QDRANT__SERVICE__HTTP_PORT=6333
@@ -38,10 +41,11 @@ services:
38
41
  - init-permissions
39
42
  - qdrant
40
43
  volumes:
41
- - ${CLAUDE_LOGS_PATH:-~/.claude/projects}:/logs:ro
42
- - ${CONFIG_PATH:-~/.claude-self-reflect/config}:/config
44
+ - ${CLAUDE_LOGS_PATH:-${HOME}/.claude/projects}:/logs:ro
45
+ - ${CONFIG_PATH:-${HOME}/.claude-self-reflect/config}:/config
43
46
  environment:
44
47
  - QDRANT_URL=http://qdrant:6333
48
+ - HF_HUB_OFFLINE=1
45
49
  - STATE_FILE=/config/imported-files.json
46
50
  - LOGS_DIR=/logs
47
51
  - OPENAI_API_KEY=${OPENAI_API_KEY:-}
@@ -67,11 +71,12 @@ services:
67
71
  - init-permissions
68
72
  - qdrant
69
73
  volumes:
70
- - ${CLAUDE_LOGS_PATH:-~/.claude/projects}:/logs:ro
71
- - ${CONFIG_PATH:-~/.claude-self-reflect/config}:/config
74
+ - ${CLAUDE_LOGS_PATH:-${HOME}/.claude/projects}:/logs:ro
75
+ - ${CONFIG_PATH:-${HOME}/.claude-self-reflect/config}:/config
72
76
  - /tmp:/tmp
73
77
  environment:
74
78
  - QDRANT_URL=http://qdrant:6333
79
+ - HF_HUB_OFFLINE=1
75
80
  - STATE_FILE=/config/imported-files.json
76
81
  - OPENAI_API_KEY=${OPENAI_API_KEY:-}
77
82
  - VOYAGE_API_KEY=${VOYAGE_API_KEY:-}
@@ -97,10 +102,11 @@ services:
97
102
  - init-permissions
98
103
  - qdrant
99
104
  volumes:
100
- - ${CLAUDE_LOGS_PATH:-~/.claude/projects}:/logs:ro
101
- - ${CONFIG_PATH:-~/.claude-self-reflect/config}:/config
105
+ - ${CLAUDE_LOGS_PATH:-${HOME}/.claude/projects}:/logs:ro
106
+ - ${CONFIG_PATH:-${HOME}/.claude-self-reflect/config}:/config
102
107
  environment:
103
108
  - QDRANT_URL=http://qdrant:6333
109
+ - HF_HUB_OFFLINE=1
104
110
  - STATE_FILE=/config/streaming-state.json # FIXED: Use streaming-specific state file
105
111
  - VOYAGE_API_KEY=${VOYAGE_API_KEY:-}
106
112
  - VOYAGE_KEY=${VOYAGE_KEY:-}
@@ -136,10 +142,11 @@ services:
136
142
  depends_on:
137
143
  - qdrant
138
144
  volumes:
139
- - ${CLAUDE_LOGS_PATH:-~/.claude/projects}:/logs:ro
140
- - ${CONFIG_PATH:-~/.claude-self-reflect/config}:/config
145
+ - ${CLAUDE_LOGS_PATH:-${HOME}/.claude/projects}:/logs:ro
146
+ - ${CONFIG_PATH:-${HOME}/.claude-self-reflect/config}:/config
141
147
  environment:
142
148
  - QDRANT_URL=http://qdrant:6333
149
+ - HF_HUB_OFFLINE=1
143
150
  - STATE_FILE=/config/imported-files.json
144
151
  - VOYAGE_API_KEY=${VOYAGE_API_KEY:-}
145
152
  - VOYAGE_KEY=${VOYAGE_KEY:-}
@@ -171,10 +178,11 @@ services:
171
178
  - init-permissions
172
179
  - qdrant
173
180
  volumes:
174
- - ${CLAUDE_LOGS_PATH:-~/.claude/projects}:/logs:ro
175
- - ${CONFIG_PATH:-~/.claude-self-reflect/config}:/config
181
+ - ${CLAUDE_LOGS_PATH:-${HOME}/.claude/projects}:/logs:ro
182
+ - ${CONFIG_PATH:-${HOME}/.claude-self-reflect/config}:/config
176
183
  environment:
177
184
  - QDRANT_URL=http://qdrant:6333
185
+ - HF_HUB_OFFLINE=1
178
186
  - STATE_FILE=/config/csr-watcher.json
179
187
  - LOGS_DIR=/logs # Fixed: Point to mounted volume
180
188
  - VOYAGE_KEY=${VOYAGE_KEY:-}
@@ -210,6 +218,7 @@ services:
210
218
  - qdrant
211
219
  environment:
212
220
  - QDRANT_URL=http://qdrant:6333
221
+ - HF_HUB_OFFLINE=1
213
222
  - VOYAGE_KEY=${VOYAGE_KEY:-}
214
223
  - PREFER_LOCAL_EMBEDDINGS=${PREFER_LOCAL_EMBEDDINGS:-true}
215
224
  - ENABLE_MEMORY_DECAY=${ENABLE_MEMORY_DECAY:-true}
@@ -221,6 +230,96 @@ services:
221
230
  tty: true
222
231
  profiles: ["mcp"]
223
232
 
233
+ # Batch watcher - Queues conversations and triggers batch narrative generation
234
+ # Requires ANTHROPIC_API_KEY for AI-powered narratives (9.3x better search quality)
235
+ # Now starts by default if ANTHROPIC_API_KEY is set
236
+ batch-watcher:
237
+ build:
238
+ context: .
239
+ dockerfile: Dockerfile.batch-watcher
240
+ container_name: claude-reflection-batch-watcher
241
+ depends_on:
242
+ - init-permissions
243
+ - qdrant
244
+ volumes:
245
+ - ${CLAUDE_LOGS_PATH:-${HOME}/.claude/projects}:/logs:ro
246
+ - ${CONFIG_PATH:-${HOME}/.claude-self-reflect/config}:/home/appuser/.claude-self-reflect/config
247
+ - ${CSR_BATCH_QUEUE_DIR:-${HOME}/.claude-self-reflect/batch_queue}:/home/appuser/.claude-self-reflect/batch_queue
248
+ - ${CSR_BATCH_STATE_DIR:-${HOME}/.claude-self-reflect/batch_state}:/home/appuser/.claude-self-reflect/batch_state
249
+ environment:
250
+ - QDRANT_URL=http://qdrant:6333
251
+ - HF_HUB_OFFLINE=1
252
+ - QDRANT_API_KEY=${QDRANT_API_KEY:-}
253
+ - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
254
+ - CSR_HOME=/home/appuser/.claude-self-reflect
255
+ - CSR_CONFIG_DIR=/home/appuser/.claude-self-reflect/config
256
+ - CSR_BATCH_QUEUE_DIR=/home/appuser/.claude-self-reflect/batch_queue
257
+ - CSR_BATCH_STATE_DIR=/home/appuser/.claude-self-reflect/batch_state
258
+ - CLAUDE_PROJECTS_DIR=/logs
259
+ - BATCH_SIZE_TRIGGER=${BATCH_SIZE_TRIGGER:-10}
260
+ - BATCH_TIME_TRIGGER_MINUTES=${BATCH_TIME_TRIGGER_MINUTES:-30}
261
+ - HOT_WINDOW_MINUTES=${HOT_WINDOW_MINUTES:-5}
262
+ - WARM_WINDOW_HOURS=${WARM_WINDOW_HOURS:-24}
263
+ - MAX_COLD_FILES=${MAX_COLD_FILES:-5}
264
+ - SUBPROCESS_TIMEOUT_SECONDS=${SUBPROCESS_TIMEOUT_SECONDS:-1800}
265
+ - PYTHONUNBUFFERED=1
266
+ - PYTHONPATH=/app
267
+ restart: unless-stopped
268
+ # profiles: ["batch-automation"] # REMOVED - now starts by default
269
+ mem_limit: 2g
270
+ memswap_limit: 2g
271
+ healthcheck:
272
+ test: ["CMD-SHELL", "kill -0 1 2>/dev/null || exit 1"]
273
+ interval: 30s
274
+ timeout: 10s
275
+ retries: 3
276
+ start_period: 10s
277
+ logging:
278
+ driver: "json-file"
279
+ options:
280
+ max-size: "10m"
281
+ max-file: "3"
282
+ labels: "service=batch-watcher"
283
+
284
+ # Batch monitor - Monitors batch API jobs and triggers evaluations
285
+ # Requires ANTHROPIC_API_KEY. Now starts by default.
286
+ batch-monitor:
287
+ build:
288
+ context: .
289
+ dockerfile: Dockerfile.batch-monitor
290
+ container_name: claude-reflection-batch-monitor
291
+ depends_on:
292
+ - init-permissions
293
+ - qdrant
294
+ volumes:
295
+ - ${CSR_BATCH_STATE_DIR:-${HOME}/.claude-self-reflect/batch_state}:/home/appuser/.claude-self-reflect/batch_state
296
+ environment:
297
+ - QDRANT_URL=http://qdrant:6333
298
+ - HF_HUB_OFFLINE=1
299
+ - QDRANT_API_KEY=${QDRANT_API_KEY:-}
300
+ - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
301
+ - CSR_HOME=/home/appuser/.claude-self-reflect
302
+ - CSR_BATCH_STATE_DIR=/home/appuser/.claude-self-reflect/batch_state
303
+ - CHECK_INTERVAL=${BATCH_MONITOR_INTERVAL:-60}
304
+ - PYTHONUNBUFFERED=1
305
+ - PYTHONPATH=/app
306
+ restart: unless-stopped
307
+ # profiles: ["batch-automation"] # REMOVED - now starts by default
308
+ mem_limit: 512m
309
+ memswap_limit: 512m
310
+ healthcheck:
311
+ test: ["CMD-SHELL", "kill -0 1 2>/dev/null || exit 1"]
312
+ interval: 30s
313
+ timeout: 10s
314
+ retries: 3
315
+ start_period: 10s
316
+ logging:
317
+ driver: "json-file"
318
+ options:
319
+ max-size: "10m"
320
+ max-file: "3"
321
+ labels: "service=batch-monitor"
322
+
224
323
  networks:
225
324
  default:
226
325
  name: claude-reflection-network
@@ -557,6 +557,109 @@ async function enrichMetadata() {
557
557
  }
558
558
  }
559
559
 
560
+ async function setupBatchAutomation() {
561
+ console.log('\nšŸš€ AI-Powered Narratives (NEW in v7.0!)...');
562
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
563
+ console.log('Transform your conversations into rich, searchable narratives.');
564
+ console.log('');
565
+ console.log('šŸ“Š Benefits:');
566
+ console.log(' • 9.3x better search quality (0.074 → 0.691 relevance score)');
567
+ console.log(' • 82% token compression while maintaining searchability');
568
+ console.log(' • 50% cost savings using Anthropic Batch API (~$0.012/conversation)');
569
+ console.log(' • Automatic extraction: tools used, files modified, concepts');
570
+ console.log('');
571
+ console.log('šŸ“ What You Get:');
572
+ console.log(' • Problem-solution structured summaries');
573
+ console.log(' • Rich metadata (tools, concepts, files)');
574
+ console.log(' • Fully automated batch processing');
575
+ console.log('');
576
+ console.log('āš™ļø How It Works:');
577
+ console.log(' 1. Background watcher queues new conversations');
578
+ console.log(' 2. Auto-triggers batch when threshold reached (default: 10)');
579
+ console.log(' 3. Anthropic Batch API generates narratives');
580
+ console.log(' 4. Enhanced narratives auto-imported to Qdrant');
581
+ console.log('');
582
+ console.log('šŸ” Privacy: Conversations sent to Anthropic Batch API for narrative generation.');
583
+ console.log(' Review: https://www.anthropic.com/privacy');
584
+ console.log('');
585
+
586
+ const enableChoice = await question('Enable AI-powered narratives? (y/n) [recommended for best search]: ');
587
+
588
+ if (enableChoice.toLowerCase() === 'y') {
589
+ console.log('\nšŸ”‘ Anthropic API Key Required');
590
+ console.log(' Get your key: https://console.anthropic.com/settings/keys');
591
+ console.log(' Cost: ~$0.012 per conversation via Batch API');
592
+
593
+ const apiKey = await question('\nPaste your Anthropic API key (sk-ant-...): ');
594
+
595
+ if (apiKey && apiKey.trim().startsWith('sk-ant-')) {
596
+ // Read current .env
597
+ const envPath = path.join(projectRoot, '.env');
598
+ let envContent = '';
599
+ try {
600
+ envContent = await fs.readFile(envPath, 'utf8');
601
+ } catch {
602
+ // File doesn't exist yet
603
+ }
604
+
605
+ // Remove existing ANTHROPIC_API_KEY if present
606
+ envContent = envContent.replace(/ANTHROPIC_API_KEY=.*/g, '');
607
+
608
+ // Add new key
609
+ envContent += `\n# Batch Automation (v7.0 AI-Powered Narratives)\nANTHROPIC_API_KEY=${apiKey.trim()}\n`;
610
+
611
+ // Write back
612
+ await fs.writeFile(envPath, envContent.trim() + '\n');
613
+
614
+ console.log('āœ… API key saved to .env');
615
+
616
+ // Start batch automation services
617
+ console.log('\nšŸš€ Starting batch automation services...');
618
+ try {
619
+ safeExec('docker', ['compose', '--profile', 'batch-automation', 'up', '-d'], {
620
+ cwd: projectRoot,
621
+ stdio: 'inherit'
622
+ });
623
+
624
+ console.log('\nāœ… Batch automation enabled!');
625
+ console.log(' • batch-watcher: Monitors for new conversations');
626
+ console.log(' • batch-monitor: Processes narrative generation');
627
+ console.log('');
628
+ console.log('šŸ“Š Monitor Progress:');
629
+ console.log(' docker compose logs batch-watcher -f');
630
+ console.log(' docker compose logs batch-monitor -f');
631
+ console.log('');
632
+ console.log('šŸŽÆ Next: New conversations will be automatically enhanced with narratives');
633
+
634
+ } catch (error) {
635
+ console.log('\nāš ļø Could not start batch services automatically');
636
+ console.log(' Start manually: docker compose --profile batch-automation up -d');
637
+ }
638
+
639
+ } else if (apiKey && apiKey.trim()) {
640
+ console.log('\nāŒ Invalid API key format. Anthropic keys start with "sk-ant-"');
641
+ console.log(' Skipping batch automation. You can enable it later by:');
642
+ console.log(' 1. Adding ANTHROPIC_API_KEY to .env');
643
+ console.log(' 2. Running: docker compose --profile batch-automation up -d');
644
+ } else {
645
+ console.log('\nšŸ“ Skipping batch automation.');
646
+ console.log(' You can enable it later by:');
647
+ console.log(' 1. Get API key: https://console.anthropic.com/settings/keys');
648
+ console.log(' 2. Add to .env: ANTHROPIC_API_KEY=sk-ant-...');
649
+ console.log(' 3. Run: docker compose --profile batch-automation up -d');
650
+ }
651
+
652
+ } else {
653
+ console.log('\nšŸ“ Skipping batch automation (staying with standard search).');
654
+ console.log(' You can enable AI narratives later by:');
655
+ console.log(' 1. Get API key: https://console.anthropic.com/settings/keys');
656
+ console.log(' 2. Add to .env: ANTHROPIC_API_KEY=sk-ant-...');
657
+ console.log(' 3. Run: docker compose --profile batch-automation up -d');
658
+ console.log('');
659
+ console.log('šŸ’” Tip: Even without narratives, you still get excellent local search!');
660
+ }
661
+ }
662
+
560
663
  async function startWatcher() {
561
664
  console.log('\nšŸ”„ Starting the streaming watcher...');
562
665
  console.log(' • HOT files (<5 min): 2-second processing');
@@ -577,28 +680,106 @@ async function startWatcher() {
577
680
  }
578
681
  }
579
682
 
683
+ async function setupRalphHooks() {
684
+ console.log('\n🐻 Ralph Loop Memory Integration (NEW!)...');
685
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
686
+ console.log('The Ralph Wiggum technique helps Claude maintain context across long sessions.');
687
+ console.log('With CSR integration, Ralph loops gain cross-session memory!');
688
+ console.log('');
689
+ console.log('šŸ“Š Benefits:');
690
+ console.log(' • Session state preserved across context compactions');
691
+ console.log(' • Past learnings automatically injected into new sessions');
692
+ console.log(' • Failed approaches remembered to avoid repeating mistakes');
693
+ console.log(' • Works with the ralph-wiggum Claude Code plugin');
694
+ console.log('');
695
+ console.log('šŸ“ How It Works:');
696
+ console.log(' • SessionStart hook: Searches CSR for past Ralph sessions');
697
+ console.log(' • PreCompact hook: Backs up state before context is lost');
698
+ console.log(' • SessionEnd hook: Stores session narrative for future reference');
699
+ console.log('');
700
+ console.log('āš™ļø Requirements:');
701
+ console.log(' • ralph-wiggum plugin (Claude Code plugin)');
702
+ console.log(' • Python 3.8+ (for hooks)');
703
+ console.log('');
704
+
705
+ const enableChoice = await question('Enable Ralph Loop memory integration? (y/n) [recommended]: ');
706
+
707
+ if (enableChoice.toLowerCase() === 'y') {
708
+ console.log('\nšŸ“¦ Installing Ralph hooks...');
709
+
710
+ try {
711
+ // Check if Python 3 is available
712
+ try {
713
+ safeExec('python3', ['--version'], { stdio: 'pipe' });
714
+ } catch {
715
+ console.log('\nāš ļø Python 3 not found');
716
+ console.log(' Ralph hooks require Python 3.8+');
717
+ console.log(' Install Python and run: ./scripts/ralph/install_hooks.sh');
718
+ return;
719
+ }
720
+
721
+ // Run the hook installation script
722
+ const installScript = join(projectRoot, 'scripts', 'ralph', 'install_hooks.sh');
723
+
724
+ // Make sure the script is executable
725
+ try {
726
+ await fs.chmod(installScript, 0o755);
727
+ } catch {
728
+ // Ignore if already executable
729
+ }
730
+
731
+ safeExec('bash', [installScript], {
732
+ cwd: projectRoot,
733
+ stdio: 'inherit'
734
+ });
735
+
736
+ console.log('\nāœ… Ralph hooks installed successfully!');
737
+ console.log('');
738
+ console.log('šŸ“‹ To use Ralph loops with memory:');
739
+ console.log(' 1. Install ralph-wiggum plugin in Claude Code:');
740
+ console.log(' /plugin install ralph-wiggum@anthropics');
741
+ console.log(' 2. Start a loop: /ralph-wiggum:ralph-loop "Your task"');
742
+ console.log(' 3. Session state will be automatically backed up to CSR');
743
+ console.log('');
744
+ console.log('šŸ“Š Verify hook installation:');
745
+ console.log(' ./scripts/ralph/install_hooks.sh --check');
746
+
747
+ } catch (error) {
748
+ console.log('\nāš ļø Could not install Ralph hooks automatically');
749
+ console.log(` Error: ${error.message}`);
750
+ console.log(' You can install manually: ./scripts/ralph/install_hooks.sh');
751
+ }
752
+
753
+ } else {
754
+ console.log('\nšŸ“ Skipping Ralph hooks installation.');
755
+ console.log(' You can install later: ./scripts/ralph/install_hooks.sh');
756
+ console.log(' Docs: https://github.com/ramakay/claude-self-reflect/blob/main/docs/development/ralph-memory-integration.md');
757
+ }
758
+ }
759
+
580
760
  async function showFinalInstructions() {
581
761
  console.log('\nāœ… Setup complete!');
582
-
762
+
583
763
  console.log('\nšŸŽÆ Your Claude Self-Reflect System:');
584
764
  console.log(' • 🌐 Qdrant Dashboard: http://localhost:6333/dashboard/');
585
765
  console.log(' • šŸ“Š Status: All services running');
586
766
  console.log(' • šŸ” Search: Semantic search with memory decay enabled');
587
767
  console.log(' • šŸš€ Watcher: HOT/WARM/COLD prioritization active');
588
-
768
+
589
769
  console.log('\nšŸ“‹ Quick Reference Commands:');
590
770
  console.log(' • Check status: docker compose ps');
591
771
  console.log(' • View logs: docker compose logs -f');
592
772
  console.log(' • Import conversations: docker compose run --rm importer');
593
773
  console.log(' • Enrich metadata: docker compose run --rm importer python /app/scripts/delta-metadata-update-safe.py');
594
774
  console.log(' • Start watcher: docker compose --profile watch up -d');
775
+ console.log(' • Ralph hooks: ./scripts/ralph/install_hooks.sh --check');
595
776
  console.log(' • Stop all: docker compose down');
596
-
777
+
597
778
  console.log('\nšŸŽÆ Next Steps:');
598
779
  console.log('1. Restart Claude Code');
599
780
  console.log('2. Look for "claude-self-reflect" in the MCP tools');
600
781
  console.log('3. Try: "Search my past conversations about Python"');
601
-
782
+
602
783
  console.log('\nšŸ“š Documentation: https://github.com/ramakay/claude-self-reflect');
603
784
  }
604
785
 
@@ -684,16 +865,22 @@ async function main() {
684
865
 
685
866
  // Configure Claude
686
867
  await configureClaude();
687
-
868
+
688
869
  // Import conversations
689
870
  await importConversations();
690
-
871
+
691
872
  // Enrich metadata (new in v2.5.19)
692
873
  await enrichMetadata();
693
-
874
+
875
+ // Setup batch automation (new in v7.0)
876
+ await setupBatchAutomation();
877
+
878
+ // Setup Ralph hooks (new in v7.1 - Memory-Augmented Ralph Loops)
879
+ await setupRalphHooks();
880
+
694
881
  // Start the watcher
695
882
  await startWatcher();
696
-
883
+
697
884
  // Show final instructions
698
885
  await showFinalInstructions();
699
886
 
@@ -94,6 +94,48 @@ class UpdateManager {
94
94
  }
95
95
  }
96
96
 
97
+ async checkRalphHooks() {
98
+ // Check if Ralph hooks are installed in ~/.claude/settings.json
99
+ const claudeSettings = path.join(this.homeDir, '.claude', 'settings.json');
100
+
101
+ try {
102
+ if (!fs.existsSync(claudeSettings)) {
103
+ return {
104
+ installed: false,
105
+ name: 'Ralph Memory Hooks',
106
+ critical: false, // Optional feature
107
+ fix: () => this.installRalphHooks()
108
+ };
109
+ }
110
+
111
+ const settings = JSON.parse(fs.readFileSync(claudeSettings, 'utf8'));
112
+
113
+ // Check for Ralph hooks in SessionStart or SessionEnd
114
+ const hasRalphHooks = settings.hooks &&
115
+ (
116
+ (settings.hooks.SessionStart &&
117
+ JSON.stringify(settings.hooks.SessionStart).includes('ralph')) ||
118
+ (settings.hooks.SessionEnd &&
119
+ JSON.stringify(settings.hooks.SessionEnd).includes('ralph'))
120
+ );
121
+
122
+ return {
123
+ installed: hasRalphHooks,
124
+ name: 'Ralph Memory Hooks',
125
+ critical: false, // Optional feature
126
+ fix: hasRalphHooks ? null : () => this.installRalphHooks()
127
+ };
128
+ } catch (error) {
129
+ return {
130
+ installed: false,
131
+ name: 'Ralph Memory Hooks',
132
+ critical: false,
133
+ fix: () => this.installRalphHooks(),
134
+ error: `Could not check Ralph hooks: ${error.message}`
135
+ };
136
+ }
137
+ }
138
+
97
139
  async checkDocker() {
98
140
  try {
99
141
  execSync('docker info', { stdio: 'ignore' });
@@ -265,6 +307,48 @@ class UpdateManager {
265
307
  }
266
308
  }
267
309
 
310
+ async installRalphHooks() {
311
+ this.log('Installing Ralph Memory Hooks...', 'info');
312
+
313
+ // Check if Python 3 is available
314
+ try {
315
+ execSync('python3 --version', { stdio: 'ignore' });
316
+ } catch {
317
+ this.log('Python 3 is required for Ralph hooks', 'error');
318
+ this.log('Install Python 3.8+ and run: ./scripts/ralph/install_hooks.sh', 'info');
319
+ return false;
320
+ }
321
+
322
+ const installScript = path.join(this.packageRoot, 'scripts', 'ralph', 'install_hooks.sh');
323
+
324
+ if (!fs.existsSync(installScript)) {
325
+ this.log(`Ralph hooks script not found: ${installScript}`, 'error');
326
+ return false;
327
+ }
328
+
329
+ try {
330
+ // Make sure the script is executable
331
+ fs.chmodSync(installScript, 0o755);
332
+
333
+ execSync(`bash "${installScript}"`, {
334
+ cwd: this.packageRoot,
335
+ stdio: 'inherit'
336
+ });
337
+
338
+ this.log('Ralph Memory Hooks installed successfully!', 'success');
339
+ this.log('', 'info');
340
+ this.log('To use Ralph loops with memory:', 'info');
341
+ this.log(' 1. Install plugin: /plugin install ralph-wiggum@anthropics', 'info');
342
+ this.log(' 2. Start a loop: /ralph-wiggum:ralph-loop "Your task"', 'info');
343
+ this.log(' 3. Session state will be automatically backed up to CSR', 'info');
344
+ return true;
345
+ } catch (error) {
346
+ this.log(`Failed to install Ralph hooks: ${error.message}`, 'error');
347
+ this.log('You can install manually: ./scripts/ralph/install_hooks.sh', 'info');
348
+ return false;
349
+ }
350
+ }
351
+
268
352
  async startQdrant() {
269
353
  this.log('Starting Qdrant...', 'info');
270
354
 
@@ -313,7 +397,8 @@ class UpdateManager {
313
397
  { name: 'Docker Config', fn: () => this.checkDockerComposeConfig() },
314
398
  { name: 'cc-statusline', fn: () => this.checkCCStatusline() },
315
399
  { name: 'csr-status', fn: () => this.checkCSRStatusScript() },
316
- { name: 'AST-Grep', fn: () => this.checkASTGrep() }
400
+ { name: 'AST-Grep', fn: () => this.checkASTGrep() },
401
+ { name: 'Ralph Hooks', fn: () => this.checkRalphHooks() }
317
402
  ];
318
403
 
319
404
  const settledResults = await Promise.allSettled(checks.map(c => c.fn()));
@@ -391,6 +476,8 @@ class UpdateManager {
391
476
  recheckResult = await this.checkCSRStatusScript();
392
477
  } else if (recheckName.includes('ast-grep')) {
393
478
  recheckResult = await this.checkASTGrep();
479
+ } else if (recheckName.includes('ralph')) {
480
+ recheckResult = await this.checkRalphHooks();
394
481
  }
395
482
 
396
483
  // Guard against undefined recheckResult (no matching verifier)