coder-config 0.44.20 → 0.44.22

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.
@@ -19,7 +19,7 @@
19
19
 
20
20
  <!-- PWA Manifest -->
21
21
  <link rel="manifest" href="/manifest.json">
22
- <script type="module" crossorigin src="/assets/index-BuO1jFRz.js"></script>
22
+ <script type="module" crossorigin src="/assets/index-kIL2LVeI.js"></script>
23
23
  <link rel="stylesheet" crossorigin href="/assets/index-DAbOgCcu.css">
24
24
  </head>
25
25
  <body>
@@ -241,37 +241,55 @@ function recordIteration(manager, id, iteration) {
241
241
  }
242
242
 
243
243
  /**
244
- * Check if loop hooks are installed (official ralph-loop plugin)
244
+ * Check if loop hooks are installed and registered in Claude Code settings
245
245
  */
246
246
  function getLoopHookStatus() {
247
- // Check for official ralph-loop plugin
248
- const pluginsDir = path.join(os.homedir(), '.claude', 'plugins', 'marketplaces');
249
- const officialPluginPath = path.join(pluginsDir, 'claude-plugins-official', 'plugins', 'ralph-loop');
250
- const stopHookPath = path.join(officialPluginPath, 'hooks', 'stop-hook.sh');
251
-
252
- // Also check custom hooks dir for backwards compatibility
253
- const customHooksDir = path.join(os.homedir(), '.claude', 'hooks');
254
- const customStopHookPath = path.join(customHooksDir, 'ralph-loop-stop.sh');
255
- const customPrepromptHookPath = path.join(customHooksDir, 'ralph-loop-preprompt.sh');
247
+ const claudeSettingsPath = path.join(os.homedir(), '.claude', 'settings.json');
248
+ const claudeHooksDir = path.join(os.homedir(), '.claude', 'hooks');
249
+ const stopHookPath = path.join(claudeHooksDir, 'ralph-loop-stop.sh');
250
+ const prepromptHookPath = path.join(claudeHooksDir, 'ralph-loop-preprompt.sh');
251
+
252
+ // Check if hook files exist
253
+ const stopHookFileExists = fs.existsSync(stopHookPath);
254
+ const prepromptHookFileExists = fs.existsSync(prepromptHookPath);
255
+
256
+ // Check if hooks are registered in settings.json
257
+ let stopHookRegistered = false;
258
+ let prepromptHookRegistered = false;
259
+
260
+ if (fs.existsSync(claudeSettingsPath)) {
261
+ try {
262
+ const settings = JSON.parse(fs.readFileSync(claudeSettingsPath, 'utf8'));
263
+ const hooks = settings.hooks || {};
264
+
265
+ // Check Stop hook registration
266
+ if (hooks.Stop) {
267
+ stopHookRegistered = hooks.Stop.some(entry =>
268
+ entry.hooks?.some(h => h.command?.includes('ralph-loop-stop.sh'))
269
+ );
270
+ }
256
271
 
257
- const officialPluginExists = fs.existsSync(officialPluginPath);
258
- const officialHookExists = fs.existsSync(stopHookPath);
272
+ // Check PreToolUse hook registration
273
+ if (hooks.PreToolUse) {
274
+ prepromptHookRegistered = hooks.PreToolUse.some(entry =>
275
+ entry.hooks?.some(h => h.command?.includes('ralph-loop-preprompt.sh'))
276
+ );
277
+ }
278
+ } catch (e) {
279
+ // Ignore parse errors
280
+ }
281
+ }
259
282
 
260
283
  const status = {
261
- // Official plugin is preferred
262
- usingOfficialPlugin: officialPluginExists,
263
- officialPlugin: {
264
- path: officialPluginPath,
265
- exists: officialPluginExists
266
- },
267
284
  stopHook: {
268
- path: officialHookExists ? stopHookPath : customStopHookPath,
269
- exists: officialHookExists || fs.existsSync(customStopHookPath)
285
+ path: stopHookPath,
286
+ exists: stopHookFileExists,
287
+ registered: stopHookRegistered
270
288
  },
271
289
  prepromptHook: {
272
- // Official plugin doesn't need preprompt hook
273
- path: customPrepromptHookPath,
274
- exists: officialPluginExists || fs.existsSync(customPrepromptHookPath)
290
+ path: prepromptHookPath,
291
+ exists: prepromptHookFileExists,
292
+ registered: prepromptHookRegistered
275
293
  }
276
294
  };
277
295
 
@@ -419,32 +437,105 @@ function fixRalphLoopPluginStructure() {
419
437
  }
420
438
 
421
439
  /**
422
- * Install loop hooks (or verify official plugin is installed)
440
+ * Install loop hooks into Claude Code's settings.json
441
+ * Registers Stop hook that increments iteration count after each response
423
442
  */
424
443
  function installLoopHooks(manager) {
425
- const pluginsDir = path.join(os.homedir(), '.claude', 'plugins', 'marketplaces');
426
- const officialPluginPath = path.join(pluginsDir, 'claude-plugins-official', 'plugins', 'ralph-loop');
444
+ const claudeSettingsPath = path.join(os.homedir(), '.claude', 'settings.json');
445
+ const claudeHooksDir = path.join(os.homedir(), '.claude', 'hooks');
446
+
447
+ // Ensure hooks directory exists
448
+ if (!fs.existsSync(claudeHooksDir)) {
449
+ fs.mkdirSync(claudeHooksDir, { recursive: true });
450
+ }
451
+
452
+ // Copy the stop hook script to ~/.claude/hooks/
453
+ const stopHookSource = path.join(__dirname, '..', '..', 'hooks', 'ralph-loop-stop.sh');
454
+ const stopHookDest = path.join(claudeHooksDir, 'ralph-loop-stop.sh');
455
+
456
+ // Also copy preprompt hook
457
+ const prepromptHookSource = path.join(__dirname, '..', '..', 'hooks', 'ralph-loop-preprompt.sh');
458
+ const prepromptHookDest = path.join(claudeHooksDir, 'ralph-loop-preprompt.sh');
459
+
460
+ try {
461
+ // Copy hook scripts
462
+ if (fs.existsSync(stopHookSource)) {
463
+ fs.copyFileSync(stopHookSource, stopHookDest);
464
+ fs.chmodSync(stopHookDest, '755');
465
+ }
466
+ if (fs.existsSync(prepromptHookSource)) {
467
+ fs.copyFileSync(prepromptHookSource, prepromptHookDest);
468
+ fs.chmodSync(prepromptHookDest, '755');
469
+ }
470
+
471
+ // Load existing settings
472
+ let settings = {};
473
+ if (fs.existsSync(claudeSettingsPath)) {
474
+ settings = JSON.parse(fs.readFileSync(claudeSettingsPath, 'utf8'));
475
+ }
476
+
477
+ // Initialize hooks if needed
478
+ if (!settings.hooks) {
479
+ settings.hooks = {};
480
+ }
481
+
482
+ // Add Stop hook for iteration tracking
483
+ const stopHookEntry = {
484
+ hooks: [{
485
+ type: 'command',
486
+ command: stopHookDest
487
+ }]
488
+ };
489
+
490
+ // Check if Stop hook already exists
491
+ if (!settings.hooks.Stop) {
492
+ settings.hooks.Stop = [stopHookEntry];
493
+ } else {
494
+ // Check if our hook is already registered
495
+ const hookExists = settings.hooks.Stop.some(entry =>
496
+ entry.hooks?.some(h => h.command?.includes('ralph-loop-stop.sh'))
497
+ );
498
+ if (!hookExists) {
499
+ settings.hooks.Stop.push(stopHookEntry);
500
+ }
501
+ }
502
+
503
+ // Add PreToolUse hook for context injection (optional)
504
+ const prepromptHookEntry = {
505
+ hooks: [{
506
+ type: 'command',
507
+ command: prepromptHookDest
508
+ }]
509
+ };
510
+
511
+ if (!settings.hooks.PreToolUse) {
512
+ settings.hooks.PreToolUse = [prepromptHookEntry];
513
+ } else {
514
+ const hookExists = settings.hooks.PreToolUse.some(entry =>
515
+ entry.hooks?.some(h => h.command?.includes('ralph-loop-preprompt.sh'))
516
+ );
517
+ if (!hookExists) {
518
+ settings.hooks.PreToolUse.push(prepromptHookEntry);
519
+ }
520
+ }
521
+
522
+ // Save updated settings
523
+ fs.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2) + '\n');
427
524
 
428
- // Check if official plugin is already installed
429
- if (fs.existsSync(officialPluginPath)) {
430
525
  return {
431
526
  success: true,
432
- message: 'Official ralph-loop plugin is already installed',
433
- usingOfficialPlugin: true,
434
- pluginPath: officialPluginPath,
435
- note: 'Loops use the /ralph-loop command from the official claude-plugins-official marketplace'
527
+ message: 'Loop hooks installed successfully',
528
+ stopHook: stopHookDest,
529
+ prepromptHook: prepromptHookDest,
530
+ note: 'Stop hook will increment iteration count after each Claude response when CODER_LOOP_ID is set'
531
+ };
532
+ } catch (error) {
533
+ return {
534
+ success: false,
535
+ error: error.message,
536
+ suggestion: 'Try manually copying hooks to ~/.claude/hooks/ and adding to ~/.claude/settings.json'
436
537
  };
437
538
  }
438
-
439
- // Suggest installing the official plugin
440
- return {
441
- success: false,
442
- error: 'Official ralph-loop plugin not found',
443
- suggestion: 'Install the claude-plugins-official marketplace to get the ralph-loop plugin:\n' +
444
- ' 1. Add marketplace: coder-config marketplace add https://github.com/anthropics/claude-plugins-official\n' +
445
- ' 2. Or manually clone: git clone https://github.com/anthropics/claude-plugins-official ~/.claude/plugins/marketplaces/claude-plugins-official',
446
- note: 'The official plugin provides /ralph-loop command with stop-hook for autonomous loops'
447
- };
448
539
  }
449
540
 
450
541
  module.exports = {