cc-hook-registry 3.0.0 → 3.2.0

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.
Files changed (3) hide show
  1. package/README.md +29 -1
  2. package/index.mjs +98 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -19,11 +19,39 @@ npx cc-hook-registry search database
19
19
  ```bash
20
20
  npx cc-hook-registry search <keyword> # Find hooks by keyword
21
21
  npx cc-hook-registry browse [category] # Browse by category
22
- npx cc-hook-registry install <id> # Install a hook
22
+ npx cc-hook-registry install <id> # Install a hook (direct download)
23
+ npx cc-hook-registry recommend # Recommend hooks for your project
23
24
  npx cc-hook-registry info <id> # Show hook details
24
25
  npx cc-hook-registry stats # Registry statistics
25
26
  ```
26
27
 
28
+ ### recommend
29
+
30
+ Scans your project for `package.json`, `requirements.txt`, `Dockerfile`, `.env`, `Gemfile`, `artisan` and recommends hooks based on your tech stack:
31
+
32
+ ```
33
+ npx cc-hook-registry recommend
34
+
35
+ ✓ destructive-guard (installed)
36
+ Essential — prevents rm -rf disasters
37
+
38
+ ○ auto-approve-build
39
+ Node.js project detected
40
+ Install: npx cc-hook-registry install auto-approve-build
41
+
42
+ ○ block-database-wipe
43
+ Prisma detected — protect against migrate reset
44
+ ```
45
+
46
+ ### install (direct download)
47
+
48
+ Hooks are downloaded directly from GitHub — no `cc-safe-setup` dependency needed:
49
+
50
+ ```bash
51
+ npx cc-hook-registry install block-database-wipe
52
+ # Downloads script, saves to ~/.claude/hooks/, registers in settings.json
53
+ ```
54
+
27
55
  ## Categories
28
56
 
29
57
  | Category | Hooks | What They Do |
package/index.mjs CHANGED
@@ -113,6 +113,8 @@ if (!command || command === '--help' || command === '-h') {
113
113
  install <id> Install a hook
114
114
  info <id> Show hook details
115
115
  recommend Recommend hooks for current project
116
+ uninstall <id> Remove an installed hook
117
+ outdated Check installed hooks for updates
116
118
  stats Registry statistics
117
119
 
118
120
  Examples:
@@ -367,6 +369,102 @@ else if (command === 'recommend') {
367
369
  console.log();
368
370
  }
369
371
 
372
+ else if (command === 'uninstall') {
373
+ const id = args[1];
374
+ if (!id) { console.log(c.red + ' Usage: cc-hook-registry uninstall <id>' + c.reset); process.exit(1); }
375
+
376
+ const hookPath = join(HOOKS_DIR, id + '.sh');
377
+ console.log();
378
+
379
+ if (!existsSync(hookPath)) {
380
+ console.log(c.red + ' Hook not installed: ' + id + c.reset);
381
+ process.exit(1);
382
+ }
383
+
384
+ // Remove script
385
+ const { unlinkSync } = await import('fs');
386
+ unlinkSync(hookPath);
387
+ console.log(c.green + ' ✓ Removed: ' + hookPath + c.reset);
388
+
389
+ // Remove from settings.json
390
+ if (existsSync(SETTINGS_PATH)) {
391
+ try {
392
+ const settings = JSON.parse(readFileSync(SETTINGS_PATH, 'utf-8'));
393
+ for (const trigger of Object.keys(settings.hooks || {})) {
394
+ settings.hooks[trigger] = settings.hooks[trigger].filter(entry =>
395
+ !(entry.hooks || []).some(h => (h.command || '').includes(id))
396
+ );
397
+ if (settings.hooks[trigger].length === 0) delete settings.hooks[trigger];
398
+ }
399
+ writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
400
+ console.log(c.green + ' ✓ Removed from settings.json' + c.reset);
401
+ } catch {}
402
+ }
403
+
404
+ console.log(c.dim + ' Restart Claude Code to take effect.' + c.reset);
405
+ console.log();
406
+ }
407
+
408
+ else if (command === 'outdated') {
409
+ console.log();
410
+ console.log(c.bold + ' Checking installed hooks for updates...' + c.reset);
411
+ console.log();
412
+
413
+ if (!existsSync(HOOKS_DIR)) {
414
+ console.log(c.dim + ' No hooks installed.' + c.reset);
415
+ process.exit(0);
416
+ }
417
+
418
+ const { readdirSync } = await import('fs');
419
+ const installed = readdirSync(HOOKS_DIR).filter(f => f.endsWith('.sh'));
420
+ let outdated = 0;
421
+ let upToDate = 0;
422
+ let unknown = 0;
423
+
424
+ for (const file of installed) {
425
+ const name = file.replace('.sh', '');
426
+ const hook = REGISTRY.find(h => h.id === name);
427
+ const localContent = readFileSync(join(HOOKS_DIR, file), 'utf-8');
428
+ const localLines = localContent.split('\n').length;
429
+
430
+ if (!hook) {
431
+ // Custom hook, not in registry
432
+ console.log(' ' + c.dim + '?' + c.reset + ' ' + file + c.dim + ' (custom, not in registry)' + c.reset);
433
+ unknown++;
434
+ continue;
435
+ }
436
+
437
+ // Check against GitHub for cc-safe-setup hooks
438
+ if (hook.source === 'cc-safe-setup' && hook.install.includes('--install-example')) {
439
+ try {
440
+ const rawUrl = `https://raw.githubusercontent.com/yurukusa/cc-safe-setup/main/examples/${name}.sh`;
441
+ const remote = execSync(`curl -sL "${rawUrl}" 2>/dev/null`, { encoding: 'utf-8', timeout: 5000 });
442
+ const remoteLines = remote.split('\n').length;
443
+
444
+ if (remote.trim() === localContent.trim()) {
445
+ console.log(' ' + c.green + '✓' + c.reset + ' ' + file + c.dim + ' (up to date)' + c.reset);
446
+ upToDate++;
447
+ } else {
448
+ const diff = remoteLines - localLines;
449
+ console.log(' ' + c.yellow + '↑' + c.reset + ' ' + file + c.yellow + ' (update available: ' + (diff > 0 ? '+' : '') + diff + ' lines)' + c.reset);
450
+ console.log(' ' + c.dim + 'Update: npx cc-hook-registry install ' + name + c.reset);
451
+ outdated++;
452
+ }
453
+ } catch {
454
+ console.log(' ' + c.dim + '?' + c.reset + ' ' + file + c.dim + ' (could not check)' + c.reset);
455
+ unknown++;
456
+ }
457
+ } else {
458
+ console.log(' ' + c.dim + '—' + c.reset + ' ' + file + c.dim + ' (external: ' + hook.source + ')' + c.reset);
459
+ unknown++;
460
+ }
461
+ }
462
+
463
+ console.log();
464
+ console.log(' ' + c.green + upToDate + ' up to date' + c.reset + ' ' + c.yellow + outdated + ' outdated' + c.reset + ' ' + c.dim + unknown + ' unchecked' + c.reset);
465
+ console.log();
466
+ }
467
+
370
468
  else if (command === 'stats') {
371
469
  const categories = {};
372
470
  const sources = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-hook-registry",
3
- "version": "3.0.0",
3
+ "version": "3.2.0",
4
4
  "description": "Search, browse, and install Claude Code hooks from the community. GitHub-based registry, no server needed.",
5
5
  "main": "index.mjs",
6
6
  "bin": {