ccjk 9.6.1 → 9.8.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 (56) hide show
  1. package/dist/chunks/boost.mjs +246 -7
  2. package/dist/chunks/ccjk-mcp.mjs +1 -1
  3. package/dist/chunks/ccr.mjs +25 -28
  4. package/dist/chunks/check-updates.mjs +4 -3
  5. package/dist/chunks/claude-code-config-manager.mjs +1 -1
  6. package/dist/chunks/claude-code-incremental-manager.mjs +1 -1
  7. package/dist/chunks/claude-config.mjs +1 -1
  8. package/dist/chunks/codex-config-switch.mjs +3 -4
  9. package/dist/chunks/codex-provider-manager.mjs +1 -2
  10. package/dist/chunks/codex.mjs +204 -3
  11. package/dist/chunks/config-switch.mjs +2 -3
  12. package/dist/chunks/config.mjs +1 -1
  13. package/dist/chunks/doctor.mjs +1 -1
  14. package/dist/chunks/features.mjs +24 -15
  15. package/dist/chunks/hook-installer.mjs +44 -0
  16. package/dist/chunks/index3.mjs +32 -32
  17. package/dist/chunks/init.mjs +129 -87
  18. package/dist/chunks/installer2.mjs +1 -1
  19. package/dist/chunks/interview.mjs +1 -1
  20. package/dist/chunks/mcp.mjs +1058 -17
  21. package/dist/chunks/menu.mjs +140 -56
  22. package/dist/chunks/package.mjs +2 -210
  23. package/dist/chunks/platform.mjs +1 -1
  24. package/dist/chunks/quick-setup.mjs +35 -18
  25. package/dist/chunks/simple-config.mjs +1 -1
  26. package/dist/{shared/ccjk.q1koQxEE.mjs → chunks/smart-defaults.mjs} +77 -79
  27. package/dist/chunks/status.mjs +208 -101
  28. package/dist/chunks/thinking.mjs +1 -1
  29. package/dist/chunks/uninstall.mjs +6 -4
  30. package/dist/chunks/update.mjs +4 -7
  31. package/dist/chunks/version-checker.mjs +1 -1
  32. package/dist/cli.mjs +4 -80
  33. package/dist/index.d.mts +17 -1482
  34. package/dist/index.d.ts +17 -1482
  35. package/dist/index.mjs +12 -4191
  36. package/dist/shared/{ccjk.CSkyCZIM.mjs → ccjk.Bndhan7G.mjs} +4 -242
  37. package/dist/shared/ccjk.CeE8RLG2.mjs +62 -0
  38. package/dist/shared/ccjk.DKojSRzw.mjs +266 -0
  39. package/dist/shared/{ccjk.CItD1fpl.mjs → ccjk.DvIrK0wz.mjs} +1 -1
  40. package/dist/shared/ccjk.LsPZ2PYo.mjs +1048 -0
  41. package/package.json +1 -1
  42. package/dist/chunks/api-adapter.mjs +0 -180
  43. package/dist/chunks/cli.mjs +0 -2227
  44. package/dist/chunks/context-menu.mjs +0 -913
  45. package/dist/chunks/hooks-sync.mjs +0 -1627
  46. package/dist/chunks/mcp-market.mjs +0 -1077
  47. package/dist/chunks/mcp-server.mjs +0 -776
  48. package/dist/chunks/project-detector.mjs +0 -131
  49. package/dist/chunks/provider-registry.mjs +0 -92
  50. package/dist/chunks/setup-wizard.mjs +0 -362
  51. package/dist/chunks/tools.mjs +0 -143
  52. package/dist/chunks/workflows2.mjs +0 -232
  53. package/dist/shared/ccjk.C0pb50xH.mjs +0 -347
  54. package/dist/shared/ccjk.ChMkBmdL.mjs +0 -490
  55. package/dist/shared/ccjk.CtSfXUSh.mjs +0 -209
  56. package/dist/shared/ccjk.xfAjmbJp.mjs +0 -75
@@ -1,1627 +0,0 @@
1
- import { homedir } from 'node:os';
2
- import ansis from 'ansis';
3
- import inquirer from 'inquirer';
4
- import { join } from 'pathe';
5
- import { i18n } from './index.mjs';
6
- import { d as displayBannerWithInfo } from '../shared/ccjk.Br91zBIG.mjs';
7
- import { a as handleExitPromptError, h as handleGeneralError } from '../shared/ccjk.CItD1fpl.mjs';
8
- import { ensureDir, writeFileAtomic, exists, readJsonFile } from './fs-operations.mjs';
9
- import 'node:fs';
10
- import 'node:process';
11
- import 'node:url';
12
- import 'i18next';
13
- import 'i18next-fs-backend';
14
- import './package.mjs';
15
- import 'node:crypto';
16
- import 'node:fs/promises';
17
-
18
- const templates = [
19
- {
20
- id: "auto-format-on-save",
21
- name: "Auto Format on Save",
22
- category: "code-quality",
23
- description: "Automatically format code files when they are saved",
24
- hook: {
25
- name: "Auto Format on Save",
26
- version: "1.0.0",
27
- trigger: {
28
- type: "file_change",
29
- pattern: "**/*.{js,ts,jsx,tsx,vue,css,scss,json,md}"
30
- },
31
- actions: [
32
- {
33
- type: "run_command",
34
- config: {
35
- command: "npx prettier --write {{file}}",
36
- workingDir: "{{projectRoot}}"
37
- }
38
- }
39
- ],
40
- privacy: "private",
41
- enabled: true
42
- },
43
- variables: [
44
- "file",
45
- "projectRoot"
46
- ]
47
- },
48
- {
49
- id: "pre-commit-lint-check",
50
- name: "Pre-commit Lint Check",
51
- category: "git",
52
- description: "Run linting checks before committing code",
53
- hook: {
54
- name: "Pre-commit Lint Check",
55
- version: "1.0.0",
56
- trigger: {
57
- type: "event",
58
- event: "pre-tool-use"
59
- },
60
- actions: [
61
- {
62
- type: "run_command",
63
- config: {
64
- command: "npm run lint",
65
- workingDir: "{{projectRoot}}",
66
- failOnError: true
67
- }
68
- }
69
- ],
70
- conditions: [
71
- {
72
- field: "tool",
73
- operator: "eq",
74
- value: "git"
75
- },
76
- {
77
- field: "command",
78
- operator: "contains",
79
- value: "commit"
80
- }
81
- ],
82
- privacy: "private",
83
- enabled: true
84
- },
85
- variables: [
86
- "projectRoot"
87
- ]
88
- },
89
- {
90
- id: "auto-generate-tests",
91
- name: "Auto Generate Tests",
92
- category: "testing",
93
- description: "Automatically generate test files for new source files",
94
- hook: {
95
- name: "Auto Generate Tests",
96
- version: "1.0.0",
97
- trigger: {
98
- type: "file_change",
99
- pattern: "src/**/*.{js,ts,jsx,tsx}"
100
- },
101
- actions: [
102
- {
103
- type: "run_command",
104
- config: {
105
- command: "npx jest --init {{file}}",
106
- workingDir: "{{projectRoot}}"
107
- }
108
- },
109
- {
110
- type: "notify",
111
- config: {
112
- title: "Test Generated",
113
- message: "Test file created for {{file}}",
114
- type: "success"
115
- }
116
- }
117
- ],
118
- conditions: [
119
- {
120
- field: "fileExists",
121
- operator: "ne",
122
- value: "{{testFile}}"
123
- }
124
- ],
125
- privacy: "private",
126
- enabled: false
127
- },
128
- variables: [
129
- "file",
130
- "projectRoot",
131
- "testFile"
132
- ]
133
- },
134
- {
135
- id: "documentation-update",
136
- name: "Documentation Update",
137
- category: "documentation",
138
- description: "Update documentation when code changes",
139
- hook: {
140
- name: "Documentation Update",
141
- version: "1.0.0",
142
- trigger: {
143
- type: "file_change",
144
- pattern: "src/**/*.{js,ts}"
145
- },
146
- actions: [
147
- {
148
- type: "run_command",
149
- config: {
150
- command: "npx typedoc --out docs {{file}}",
151
- workingDir: "{{projectRoot}}"
152
- }
153
- },
154
- {
155
- type: "notify",
156
- config: {
157
- title: "Documentation Updated",
158
- message: "API documentation regenerated",
159
- type: "info"
160
- }
161
- }
162
- ],
163
- privacy: "private",
164
- enabled: false
165
- },
166
- variables: [
167
- "file",
168
- "projectRoot"
169
- ]
170
- },
171
- {
172
- id: "dependency-check",
173
- name: "Dependency Security Check",
174
- category: "security",
175
- description: "Check for security vulnerabilities in dependencies",
176
- hook: {
177
- name: "Dependency Security Check",
178
- version: "1.0.0",
179
- trigger: {
180
- type: "file_change",
181
- pattern: "{package.json,package-lock.json,pnpm-lock.yaml,yarn.lock}"
182
- },
183
- actions: [
184
- {
185
- type: "run_command",
186
- config: {
187
- command: "npm audit --audit-level=moderate",
188
- workingDir: "{{projectRoot}}",
189
- continueOnError: true
190
- }
191
- },
192
- {
193
- type: "notify",
194
- config: {
195
- title: "Security Audit Complete",
196
- message: "Dependency security check finished",
197
- type: "info"
198
- }
199
- }
200
- ],
201
- privacy: "private",
202
- enabled: true
203
- },
204
- variables: [
205
- "projectRoot"
206
- ]
207
- },
208
- {
209
- id: "security-scan",
210
- name: "Code Security Scan",
211
- category: "security",
212
- description: "Scan code for security vulnerabilities",
213
- hook: {
214
- name: "Code Security Scan",
215
- version: "1.0.0",
216
- trigger: {
217
- type: "schedule",
218
- schedule: "0 0 * * *"
219
- },
220
- actions: [
221
- {
222
- type: "run_command",
223
- config: {
224
- command: "npx eslint --ext .js,.ts --plugin security .",
225
- workingDir: "{{projectRoot}}",
226
- continueOnError: true
227
- }
228
- },
229
- {
230
- type: "call_api",
231
- config: {
232
- url: "{{webhookUrl}}",
233
- method: "POST",
234
- body: {
235
- type: "security-scan",
236
- status: "{{status}}",
237
- timestamp: "{{timestamp}}"
238
- }
239
- }
240
- }
241
- ],
242
- privacy: "private",
243
- enabled: false
244
- },
245
- variables: [
246
- "projectRoot",
247
- "webhookUrl",
248
- "status",
249
- "timestamp"
250
- ]
251
- },
252
- {
253
- id: "build-on-push",
254
- name: "Build on Git Push",
255
- category: "git",
256
- description: "Automatically build project when pushing to git",
257
- hook: {
258
- name: "Build on Git Push",
259
- version: "1.0.0",
260
- trigger: {
261
- type: "event",
262
- event: "pre-tool-use"
263
- },
264
- actions: [
265
- {
266
- type: "run_command",
267
- config: {
268
- command: "npm run build",
269
- workingDir: "{{projectRoot}}",
270
- failOnError: true
271
- }
272
- },
273
- {
274
- type: "notify",
275
- config: {
276
- title: "Build Complete",
277
- message: "Project built successfully before push",
278
- type: "success"
279
- }
280
- }
281
- ],
282
- conditions: [
283
- {
284
- field: "tool",
285
- operator: "eq",
286
- value: "git"
287
- },
288
- {
289
- field: "command",
290
- operator: "contains",
291
- value: "push"
292
- }
293
- ],
294
- privacy: "private",
295
- enabled: false
296
- },
297
- variables: [
298
- "projectRoot"
299
- ]
300
- },
301
- {
302
- id: "test-before-commit",
303
- name: "Test Before Commit",
304
- category: "testing",
305
- description: "Run tests before committing code",
306
- hook: {
307
- name: "Test Before Commit",
308
- version: "1.0.0",
309
- trigger: {
310
- type: "event",
311
- event: "pre-tool-use"
312
- },
313
- actions: [
314
- {
315
- type: "run_command",
316
- config: {
317
- command: "npm test",
318
- workingDir: "{{projectRoot}}",
319
- failOnError: true
320
- }
321
- }
322
- ],
323
- conditions: [
324
- {
325
- field: "tool",
326
- operator: "eq",
327
- value: "git"
328
- },
329
- {
330
- field: "command",
331
- operator: "contains",
332
- value: "commit"
333
- }
334
- ],
335
- privacy: "private",
336
- enabled: false
337
- },
338
- variables: [
339
- "projectRoot"
340
- ]
341
- },
342
- {
343
- id: "changelog-update",
344
- name: "Changelog Update",
345
- category: "documentation",
346
- description: "Update changelog when version changes",
347
- hook: {
348
- name: "Changelog Update",
349
- version: "1.0.0",
350
- trigger: {
351
- type: "file_change",
352
- pattern: "package.json"
353
- },
354
- actions: [
355
- {
356
- type: "run_command",
357
- config: {
358
- command: "npx conventional-changelog -p angular -i CHANGELOG.md -s",
359
- workingDir: "{{projectRoot}}"
360
- }
361
- },
362
- {
363
- type: "notify",
364
- config: {
365
- title: "Changelog Updated",
366
- message: "CHANGELOG.md has been updated",
367
- type: "info"
368
- }
369
- }
370
- ],
371
- conditions: [
372
- {
373
- field: "versionChanged",
374
- operator: "eq",
375
- value: "true"
376
- }
377
- ],
378
- privacy: "private",
379
- enabled: false
380
- },
381
- variables: [
382
- "projectRoot"
383
- ]
384
- },
385
- {
386
- id: "notify-on-error",
387
- name: "Notify on Error",
388
- category: "notification",
389
- description: "Send notification when an error occurs",
390
- hook: {
391
- name: "Notify on Error",
392
- version: "1.0.0",
393
- trigger: {
394
- type: "event",
395
- event: "error"
396
- },
397
- actions: [
398
- {
399
- type: "notify",
400
- config: {
401
- title: "Error Occurred",
402
- message: "{{errorMessage}}",
403
- type: "error",
404
- priority: "high"
405
- }
406
- },
407
- {
408
- type: "call_api",
409
- config: {
410
- url: "{{webhookUrl}}",
411
- method: "POST",
412
- body: {
413
- type: "error",
414
- message: "{{errorMessage}}",
415
- stack: "{{errorStack}}",
416
- timestamp: "{{timestamp}}"
417
- }
418
- }
419
- }
420
- ],
421
- privacy: "private",
422
- enabled: true
423
- },
424
- variables: [
425
- "errorMessage",
426
- "errorStack",
427
- "timestamp",
428
- "webhookUrl"
429
- ]
430
- },
431
- {
432
- id: "task-complete-notification",
433
- name: "Task Complete Notification",
434
- category: "notification",
435
- description: "Send notification when a task completes",
436
- hook: {
437
- name: "Task Complete Notification",
438
- version: "1.0.0",
439
- trigger: {
440
- type: "event",
441
- event: "task-complete"
442
- },
443
- actions: [
444
- {
445
- type: "notify",
446
- config: {
447
- title: "Task Complete",
448
- message: "{{taskDescription}} completed in {{duration}}ms",
449
- type: "success"
450
- }
451
- }
452
- ],
453
- privacy: "private",
454
- enabled: true
455
- },
456
- variables: [
457
- "taskDescription",
458
- "duration"
459
- ]
460
- },
461
- {
462
- id: "workflow-start-log",
463
- name: "Workflow Start Logger",
464
- category: "logging",
465
- description: "Log when a workflow starts",
466
- hook: {
467
- name: "Workflow Start Logger",
468
- version: "1.0.0",
469
- trigger: {
470
- type: "event",
471
- event: "workflow-start"
472
- },
473
- actions: [
474
- {
475
- type: "run_command",
476
- config: {
477
- command: "echo '[{{timestamp}}] Workflow {{workflowId}} started' >> workflow.log",
478
- workingDir: "{{projectRoot}}"
479
- }
480
- }
481
- ],
482
- privacy: "private",
483
- enabled: false
484
- },
485
- variables: [
486
- "workflowId",
487
- "timestamp",
488
- "projectRoot"
489
- ]
490
- },
491
- {
492
- id: "backup-on-change",
493
- name: "Backup on File Change",
494
- category: "backup",
495
- description: "Create backup when important files change",
496
- hook: {
497
- name: "Backup on File Change",
498
- version: "1.0.0",
499
- trigger: {
500
- type: "file_change",
501
- pattern: "{.env,.env.local,config/*.json}"
502
- },
503
- actions: [
504
- {
505
- type: "run_command",
506
- config: {
507
- command: "cp {{file}} {{file}}.backup.{{timestamp}}",
508
- workingDir: "{{projectRoot}}"
509
- }
510
- },
511
- {
512
- type: "notify",
513
- config: {
514
- title: "Backup Created",
515
- message: "Backup created for {{file}}",
516
- type: "info"
517
- }
518
- }
519
- ],
520
- privacy: "private",
521
- enabled: false
522
- },
523
- variables: [
524
- "file",
525
- "timestamp",
526
- "projectRoot"
527
- ]
528
- },
529
- {
530
- id: "code-review-reminder",
531
- name: "Code Review Reminder",
532
- category: "collaboration",
533
- description: "Remind to request code review before pushing",
534
- hook: {
535
- name: "Code Review Reminder",
536
- version: "1.0.0",
537
- trigger: {
538
- type: "event",
539
- event: "pre-tool-use"
540
- },
541
- actions: [
542
- {
543
- type: "notify",
544
- config: {
545
- title: "Code Review Reminder",
546
- message: "Don't forget to request code review!",
547
- type: "info"
548
- }
549
- }
550
- ],
551
- conditions: [
552
- {
553
- field: "tool",
554
- operator: "eq",
555
- value: "git"
556
- },
557
- {
558
- field: "command",
559
- operator: "contains",
560
- value: "push"
561
- },
562
- {
563
- field: "branch",
564
- operator: "ne",
565
- value: "main"
566
- }
567
- ],
568
- privacy: "private",
569
- enabled: false
570
- },
571
- variables: [
572
- "branch"
573
- ]
574
- },
575
- {
576
- id: "performance-monitor",
577
- name: "Performance Monitor",
578
- category: "monitoring",
579
- description: "Monitor and log performance metrics",
580
- hook: {
581
- name: "Performance Monitor",
582
- version: "1.0.0",
583
- trigger: {
584
- type: "event",
585
- event: "task-complete"
586
- },
587
- actions: [
588
- {
589
- type: "transform",
590
- config: {
591
- input: "{{taskDuration}}",
592
- transform: "log-performance",
593
- output: "performance.log"
594
- }
595
- },
596
- {
597
- type: "call_api",
598
- config: {
599
- url: "{{metricsUrl}}",
600
- method: "POST",
601
- body: {
602
- task: "{{taskId}}",
603
- duration: "{{taskDuration}}",
604
- timestamp: "{{timestamp}}"
605
- }
606
- }
607
- }
608
- ],
609
- conditions: [
610
- {
611
- field: "taskDuration",
612
- operator: "gt",
613
- value: "1000"
614
- }
615
- ],
616
- privacy: "private",
617
- enabled: false
618
- },
619
- variables: [
620
- "taskId",
621
- "taskDuration",
622
- "timestamp",
623
- "metricsUrl"
624
- ]
625
- }
626
- ];
627
- const categories = [
628
- {
629
- id: "code-quality",
630
- name: {
631
- en: "Code Quality",
632
- "zh-CN": "代码质量"
633
- },
634
- description: {
635
- en: "Hooks for maintaining code quality and formatting",
636
- "zh-CN": "用于维护代码质量和格式化的钩子"
637
- },
638
- icon: "✨"
639
- },
640
- {
641
- id: "git",
642
- name: {
643
- en: "Git Workflow",
644
- "zh-CN": "Git 工作流"
645
- },
646
- description: {
647
- en: "Hooks for Git operations and version control",
648
- "zh-CN": "用于 Git 操作和版本控制的钩子"
649
- },
650
- icon: "🔀"
651
- },
652
- {
653
- id: "testing",
654
- name: {
655
- en: "Testing",
656
- "zh-CN": "测试"
657
- },
658
- description: {
659
- en: "Hooks for automated testing workflows",
660
- "zh-CN": "用于自动化测试工作流的钩子"
661
- },
662
- icon: "🧪"
663
- },
664
- {
665
- id: "documentation",
666
- name: {
667
- en: "Documentation",
668
- "zh-CN": "文档"
669
- },
670
- description: {
671
- en: "Hooks for documentation generation and updates",
672
- "zh-CN": "用于文档生成和更新的钩子"
673
- },
674
- icon: "📚"
675
- },
676
- {
677
- id: "security",
678
- name: {
679
- en: "Security",
680
- "zh-CN": "安全"
681
- },
682
- description: {
683
- en: "Hooks for security scanning and vulnerability checks",
684
- "zh-CN": "用于安全扫描和漏洞检查的钩子"
685
- },
686
- icon: "🔒"
687
- },
688
- {
689
- id: "notification",
690
- name: {
691
- en: "Notifications",
692
- "zh-CN": "通知"
693
- },
694
- description: {
695
- en: "Hooks for sending notifications and alerts",
696
- "zh-CN": "用于发送通知和警报的钩子"
697
- },
698
- icon: "🔔"
699
- },
700
- {
701
- id: "logging",
702
- name: {
703
- en: "Logging",
704
- "zh-CN": "日志"
705
- },
706
- description: {
707
- en: "Hooks for logging and audit trails",
708
- "zh-CN": "用于日志记录和审计跟踪的钩子"
709
- },
710
- icon: "📝"
711
- },
712
- {
713
- id: "backup",
714
- name: {
715
- en: "Backup",
716
- "zh-CN": "备份"
717
- },
718
- description: {
719
- en: "Hooks for automated backups",
720
- "zh-CN": "用于自动备份的钩子"
721
- },
722
- icon: "💾"
723
- },
724
- {
725
- id: "collaboration",
726
- name: {
727
- en: "Collaboration",
728
- "zh-CN": "协作"
729
- },
730
- description: {
731
- en: "Hooks for team collaboration workflows",
732
- "zh-CN": "用于团队协作工作流的钩子"
733
- },
734
- icon: "👥"
735
- },
736
- {
737
- id: "monitoring",
738
- name: {
739
- en: "Monitoring",
740
- "zh-CN": "监控"
741
- },
742
- description: {
743
- en: "Hooks for performance and system monitoring",
744
- "zh-CN": "用于性能和系统监控的钩子"
745
- },
746
- icon: "📊"
747
- }
748
- ];
749
- const hookTemplatesData = {
750
- templates: templates,
751
- categories: categories
752
- };
753
-
754
- class CloudHooksSyncClient {
755
- baseUrl;
756
- apiKey;
757
- timeout;
758
- enableLogging;
759
- constructor(options = {}) {
760
- this.baseUrl = options.baseUrl || "https://api.api.claudehome.cn/v1/hooks";
761
- this.apiKey = options.apiKey;
762
- this.timeout = options.timeout || 3e4;
763
- this.enableLogging = options.enableLogging || false;
764
- }
765
- // ==========================================================================
766
- // Public API Methods
767
- // ==========================================================================
768
- /**
769
- * Upload hooks to cloud
770
- *
771
- * @param hooks - Hooks to upload
772
- * @returns Upload result
773
- */
774
- async uploadHooks(hooks) {
775
- this.log("Uploading hooks:", hooks.length);
776
- return this.request("/upload", {
777
- method: "POST",
778
- body: JSON.stringify({ hooks })
779
- });
780
- }
781
- /**
782
- * Download hooks from cloud
783
- *
784
- * @param options - Download options
785
- * @param options.privacy - Filter by privacy level
786
- * @param options.category - Filter by category
787
- * @param options.tags - Filter by tags
788
- * @returns Downloaded hooks
789
- */
790
- async downloadHooks(options) {
791
- this.log("Downloading hooks with options:", options);
792
- return this.request("/download", {
793
- method: "GET",
794
- params: options
795
- });
796
- }
797
- /**
798
- * Sync hooks bidirectionally
799
- *
800
- * @param localHooks - Local hooks to sync
801
- * @param options - Sync options
802
- * @returns Sync result
803
- */
804
- async syncHooks(localHooks, options) {
805
- this.log("Syncing hooks:", localHooks.length, "options:", options);
806
- return this.request("/sync", {
807
- method: "POST",
808
- body: JSON.stringify({ hooks: localHooks, options })
809
- });
810
- }
811
- /**
812
- * Get hook templates
813
- *
814
- * @param options - Filter options
815
- * @param options.category - Filter by category
816
- * @param options.tags - Filter by tags
817
- * @param options.language - Filter by language
818
- * @returns Hook templates
819
- */
820
- async getTemplates(options) {
821
- this.log("Getting templates with options:", options);
822
- return this.request("/templates", {
823
- method: "GET",
824
- params: options
825
- });
826
- }
827
- /**
828
- * Get a specific hook template
829
- *
830
- * @param id - Template ID
831
- * @returns Hook template
832
- */
833
- async getTemplate(id) {
834
- this.log("Getting template:", id);
835
- return this.request(`/templates/${id}`, {
836
- method: "GET"
837
- });
838
- }
839
- /**
840
- * Get hook by ID
841
- *
842
- * @param id - Hook ID
843
- * @returns Hook details
844
- */
845
- async getHook(id) {
846
- this.log("Getting hook:", id);
847
- return this.request(`/hooks/${id}`, {
848
- method: "GET"
849
- });
850
- }
851
- /**
852
- * Delete hook from cloud
853
- *
854
- * @param id - Hook ID
855
- * @returns Deletion result
856
- */
857
- async deleteHook(id) {
858
- this.log("Deleting hook:", id);
859
- return this.request(`/hooks/${id}`, {
860
- method: "DELETE"
861
- });
862
- }
863
- /**
864
- * Get hook execution logs
865
- *
866
- * @param hookId - Hook ID
867
- * @param options - Filter options
868
- * @param options.limit - Maximum number of logs to return
869
- * @param options.offset - Offset for pagination
870
- * @param options.status - Filter by execution status
871
- * @returns Execution logs
872
- */
873
- async getExecutionLogs(hookId, options) {
874
- this.log("Getting execution logs for hook:", hookId, "options:", options);
875
- return this.request(`/hooks/${hookId}/logs`, {
876
- method: "GET",
877
- params: options
878
- });
879
- }
880
- /**
881
- * Enable/disable hook
882
- *
883
- * @param id - Hook ID
884
- * @param enabled - Whether to enable or disable
885
- * @returns Update result
886
- */
887
- async setHookEnabled(id, enabled) {
888
- this.log("Setting hook enabled:", id, enabled);
889
- return this.request(`/hooks/${id}/enabled`, {
890
- method: "PUT",
891
- body: JSON.stringify({ enabled })
892
- });
893
- }
894
- // ==========================================================================
895
- // Private Helper Methods
896
- // ==========================================================================
897
- /**
898
- * Make HTTP request to cloud service
899
- */
900
- async request(endpoint, options = {}) {
901
- const url = this.buildUrl(endpoint, options.params);
902
- try {
903
- const response = await fetch(url, {
904
- method: options.method || "GET",
905
- headers: this.getHeaders(),
906
- body: options.body,
907
- signal: AbortSignal.timeout(this.timeout)
908
- });
909
- const data = await response.json();
910
- if (!response.ok) {
911
- return {
912
- success: false,
913
- error: data.error || `HTTP ${response.status}: ${response.statusText}`,
914
- code: data.code || `HTTP_${response.status}`
915
- };
916
- }
917
- return {
918
- ...data,
919
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
920
- };
921
- } catch (error) {
922
- if (error instanceof Error) {
923
- return {
924
- success: false,
925
- error: error.message,
926
- code: "NETWORK_ERROR"
927
- };
928
- }
929
- return {
930
- success: false,
931
- error: String(error),
932
- code: "UNKNOWN_ERROR"
933
- };
934
- }
935
- }
936
- /**
937
- * Build full URL with query parameters
938
- */
939
- buildUrl(endpoint, params) {
940
- const url = new URL(endpoint, this.baseUrl);
941
- if (params) {
942
- for (const [key, value] of Object.entries(params)) {
943
- if (value !== void 0 && value !== null) {
944
- if (Array.isArray(value)) {
945
- value.forEach((v) => url.searchParams.append(key, String(v)));
946
- } else {
947
- url.searchParams.append(key, String(value));
948
- }
949
- }
950
- }
951
- }
952
- return url.toString();
953
- }
954
- /**
955
- * Get request headers
956
- */
957
- getHeaders() {
958
- const headers = {
959
- "Content-Type": "application/json",
960
- "User-Agent": "CCJK-Hooks-Sync/1.0"
961
- };
962
- if (this.apiKey) {
963
- headers.Authorization = `Bearer ${this.apiKey}`;
964
- }
965
- return headers;
966
- }
967
- /**
968
- * Log message (if logging is enabled)
969
- */
970
- log(...args) {
971
- if (this.enableLogging) {
972
- console.log("[CloudHooksSyncClient]", ...args);
973
- }
974
- }
975
- }
976
- function convertFromCloudHook(cloudHook) {
977
- return {
978
- id: cloudHook.id,
979
- name: cloudHook.name,
980
- description: cloudHook.metadata.description,
981
- type: cloudHook.trigger.event || "pre-tool-use",
982
- priority: 5,
983
- condition: cloudHook.conditions ? convertFromCloudConditions(cloudHook.conditions) : void 0,
984
- action: {
985
- execute: async () => {
986
- return {
987
- success: true,
988
- status: "success",
989
- durationMs: 0,
990
- continueChain: true
991
- };
992
- }
993
- },
994
- enabled: cloudHook.enabled,
995
- source: cloudHook.metadata.category,
996
- version: cloudHook.version,
997
- author: cloudHook.metadata.author,
998
- tags: cloudHook.metadata.tags
999
- };
1000
- }
1001
- function convertFromCloudConditions(conditions) {
1002
- const condition = {};
1003
- for (const cond of conditions) {
1004
- if (cond.field === "tool") {
1005
- condition.tool = cond.operator === "matches" ? new RegExp(cond.value) : cond.value;
1006
- } else if (cond.field === "skillId") {
1007
- condition.skillId = cond.operator === "matches" ? new RegExp(cond.value) : cond.value;
1008
- } else if (cond.field === "workflowId") {
1009
- condition.workflowId = cond.operator === "matches" ? new RegExp(cond.value) : cond.value;
1010
- }
1011
- }
1012
- return condition;
1013
- }
1014
-
1015
- class HookRegistry {
1016
- state;
1017
- constructor() {
1018
- this.state = {
1019
- version: "1.0.0",
1020
- hooks: /* @__PURE__ */ new Map(),
1021
- hooksByType: /* @__PURE__ */ new Map(),
1022
- hooksByTool: /* @__PURE__ */ new Map(),
1023
- lastUpdated: /* @__PURE__ */ new Date()
1024
- };
1025
- }
1026
- /**
1027
- * Register a hook
1028
- *
1029
- * @param hook - Hook to register
1030
- * @param options - Registration options
1031
- * @returns Whether registration was successful
1032
- */
1033
- register(hook, options) {
1034
- const existingEntry = this.state.hooks.get(hook.id);
1035
- if (existingEntry && !options?.overwrite) {
1036
- return false;
1037
- }
1038
- const entry = {
1039
- hook: {
1040
- ...hook,
1041
- enabled: options?.enabled ?? hook.enabled
1042
- },
1043
- registeredAt: /* @__PURE__ */ new Date(),
1044
- source: options?.source ?? hook.source,
1045
- executionCount: 0,
1046
- failureCount: 0
1047
- };
1048
- this.state.hooks.set(hook.id, entry);
1049
- const typeHooks = this.state.hooksByType.get(hook.type) ?? [];
1050
- if (!typeHooks.includes(hook.id)) {
1051
- typeHooks.push(hook.id);
1052
- this.state.hooksByType.set(hook.type, typeHooks);
1053
- }
1054
- if (hook.condition?.tool && typeof hook.condition.tool === "string") {
1055
- const toolHooks = this.state.hooksByTool.get(hook.condition.tool) ?? [];
1056
- if (!toolHooks.includes(hook.id)) {
1057
- toolHooks.push(hook.id);
1058
- this.state.hooksByTool.set(hook.condition.tool, toolHooks);
1059
- }
1060
- }
1061
- this.state.lastUpdated = /* @__PURE__ */ new Date();
1062
- return true;
1063
- }
1064
- /**
1065
- * Unregister a hook
1066
- *
1067
- * @param hookId - ID of hook to unregister
1068
- * @returns Whether unregistration was successful
1069
- */
1070
- unregister(hookId) {
1071
- const entry = this.state.hooks.get(hookId);
1072
- if (!entry) {
1073
- return false;
1074
- }
1075
- this.state.hooks.delete(hookId);
1076
- const typeHooks = this.state.hooksByType.get(entry.hook.type);
1077
- if (typeHooks) {
1078
- const index = typeHooks.indexOf(hookId);
1079
- if (index !== -1) {
1080
- typeHooks.splice(index, 1);
1081
- }
1082
- }
1083
- if (entry.hook.condition?.tool && typeof entry.hook.condition.tool === "string") {
1084
- const toolHooks = this.state.hooksByTool.get(entry.hook.condition.tool);
1085
- if (toolHooks) {
1086
- const index = toolHooks.indexOf(hookId);
1087
- if (index !== -1) {
1088
- toolHooks.splice(index, 1);
1089
- }
1090
- }
1091
- }
1092
- this.state.lastUpdated = /* @__PURE__ */ new Date();
1093
- return true;
1094
- }
1095
- /**
1096
- * Get a hook by ID
1097
- *
1098
- * @param hookId - Hook ID
1099
- * @returns Hook entry or undefined
1100
- */
1101
- get(hookId) {
1102
- return this.state.hooks.get(hookId);
1103
- }
1104
- /**
1105
- * Get hooks for a specific type
1106
- *
1107
- * @param type - Hook type
1108
- * @returns Array of hooks sorted by priority
1109
- */
1110
- getHooksForType(type) {
1111
- const hookIds = this.state.hooksByType.get(type) ?? [];
1112
- return hookIds.map((id) => this.state.hooks.get(id)?.hook).filter((hook) => hook !== void 0).sort((a, b) => (b.priority ?? 5) - (a.priority ?? 5));
1113
- }
1114
- /**
1115
- * Get hooks for a specific tool
1116
- *
1117
- * @param tool - Tool name
1118
- * @returns Array of hooks sorted by priority
1119
- */
1120
- getHooksForTool(tool) {
1121
- const hookIds = this.state.hooksByTool.get(tool) ?? [];
1122
- return hookIds.map((id) => this.state.hooks.get(id)?.hook).filter((hook) => hook !== void 0).sort((a, b) => (b.priority ?? 5) - (a.priority ?? 5));
1123
- }
1124
- /**
1125
- * Filter hooks based on options
1126
- *
1127
- * @param options - Filter options
1128
- * @returns Array of matching hooks
1129
- */
1130
- filter(options) {
1131
- let hooks = Array.from(this.state.hooks.values()).map((entry) => entry.hook);
1132
- if (options.type) {
1133
- hooks = hooks.filter((h) => h.type === options.type);
1134
- }
1135
- if (options.enabled !== void 0) {
1136
- hooks = hooks.filter((h) => h.enabled === options.enabled);
1137
- }
1138
- if (options.source) {
1139
- hooks = hooks.filter((h) => h.source === options.source);
1140
- }
1141
- if (options.tags && options.tags.length > 0) {
1142
- hooks = hooks.filter(
1143
- (h) => options.tags.every((tag) => h.tags?.includes(tag))
1144
- );
1145
- }
1146
- if (options.priorityRange) {
1147
- const { min, max } = options.priorityRange;
1148
- hooks = hooks.filter((h) => {
1149
- const priority = h.priority ?? 5;
1150
- if (min !== void 0 && priority < min)
1151
- return false;
1152
- if (max !== void 0 && priority > max)
1153
- return false;
1154
- return true;
1155
- });
1156
- }
1157
- return hooks.sort((a, b) => (b.priority ?? 5) - (a.priority ?? 5));
1158
- }
1159
- /**
1160
- * Enable a hook
1161
- *
1162
- * @param hookId - Hook ID
1163
- * @returns Whether operation was successful
1164
- */
1165
- enable(hookId) {
1166
- const entry = this.state.hooks.get(hookId);
1167
- if (!entry)
1168
- return false;
1169
- entry.hook.enabled = true;
1170
- this.state.lastUpdated = /* @__PURE__ */ new Date();
1171
- return true;
1172
- }
1173
- /**
1174
- * Disable a hook
1175
- *
1176
- * @param hookId - Hook ID
1177
- * @returns Whether operation was successful
1178
- */
1179
- disable(hookId) {
1180
- const entry = this.state.hooks.get(hookId);
1181
- if (!entry)
1182
- return false;
1183
- entry.hook.enabled = false;
1184
- this.state.lastUpdated = /* @__PURE__ */ new Date();
1185
- return true;
1186
- }
1187
- /**
1188
- * Update hook execution statistics
1189
- *
1190
- * @param hookId - Hook ID
1191
- * @param success - Whether execution was successful
1192
- * @param result - Execution result
1193
- */
1194
- updateStats(hookId, success, result) {
1195
- const entry = this.state.hooks.get(hookId);
1196
- if (!entry)
1197
- return;
1198
- entry.executionCount++;
1199
- if (!success) {
1200
- entry.failureCount++;
1201
- }
1202
- entry.lastExecutedAt = /* @__PURE__ */ new Date();
1203
- entry.lastResult = result;
1204
- }
1205
- /**
1206
- * Get registry statistics
1207
- *
1208
- * @returns Hook statistics
1209
- */
1210
- getStatistics() {
1211
- const entries = Array.from(this.state.hooks.values());
1212
- const hooks = entries.map((e) => e.hook);
1213
- const hooksByType = {
1214
- "pre-tool-use": 0,
1215
- "post-tool-use": 0,
1216
- "skill-activate": 0,
1217
- "skill-complete": 0,
1218
- "workflow-start": 0,
1219
- "workflow-complete": 0,
1220
- "config-change": 0,
1221
- "error": 0,
1222
- "task-start": 0,
1223
- "task-complete": 0,
1224
- "task-failed": 0,
1225
- "task-progress": 0
1226
- };
1227
- const hooksBySource = {};
1228
- for (const hook of hooks) {
1229
- hooksByType[hook.type]++;
1230
- hooksBySource[hook.source] = (hooksBySource[hook.source] ?? 0) + 1;
1231
- }
1232
- const totalExecutions = entries.reduce((sum, e) => sum + e.executionCount, 0);
1233
- const totalFailures = entries.reduce((sum, e) => sum + e.failureCount, 0);
1234
- const mostExecuted = entries.filter((e) => e.executionCount > 0).sort((a, b) => b.executionCount - a.executionCount).slice(0, 5).map((e) => ({ hookId: e.hook.id, executionCount: e.executionCount }));
1235
- const mostFailed = entries.filter((e) => e.failureCount > 0).sort((a, b) => b.failureCount - a.failureCount).slice(0, 5).map((e) => ({ hookId: e.hook.id, failureCount: e.failureCount }));
1236
- return {
1237
- totalHooks: hooks.length,
1238
- enabledHooks: hooks.filter((h) => h.enabled).length,
1239
- disabledHooks: hooks.filter((h) => !h.enabled).length,
1240
- totalExecutions,
1241
- totalFailures,
1242
- averageExecutionMs: 0,
1243
- // Would need to track this separately
1244
- hooksByType,
1245
- hooksBySource,
1246
- mostExecuted,
1247
- mostFailed
1248
- };
1249
- }
1250
- /**
1251
- * Clear all hooks
1252
- */
1253
- clear() {
1254
- this.state.hooks.clear();
1255
- this.state.hooksByType.clear();
1256
- this.state.hooksByTool.clear();
1257
- this.state.lastUpdated = /* @__PURE__ */ new Date();
1258
- }
1259
- /**
1260
- * Get all hooks
1261
- *
1262
- * @returns Array of all hooks
1263
- */
1264
- getAll() {
1265
- return Array.from(this.state.hooks.values()).map((entry) => entry.hook);
1266
- }
1267
- }
1268
- let globalRegistry = null;
1269
- function getGlobalRegistry() {
1270
- if (!globalRegistry) {
1271
- globalRegistry = new HookRegistry();
1272
- }
1273
- return globalRegistry;
1274
- }
1275
-
1276
- const HOOKS_DIR = join(homedir(), ".ccjk", "hooks");
1277
- const HOOKS_STORAGE_FILE = join(HOOKS_DIR, "hooks.json");
1278
- async function hooksSync(options = {}) {
1279
- try {
1280
- if (!options.skipBanner) {
1281
- displayBannerWithInfo();
1282
- }
1283
- const lang = options.lang || "zh-CN";
1284
- if (options.action) {
1285
- switch (options.action) {
1286
- case "sync":
1287
- await syncHooks();
1288
- break;
1289
- case "list":
1290
- await listHooks(options);
1291
- break;
1292
- case "enable":
1293
- if (!options.hookId) {
1294
- console.log(ansis.red(i18n.t("menu:hooksSync.errors.hookIdRequired")));
1295
- return;
1296
- }
1297
- await toggleHook(options.hookId, true);
1298
- break;
1299
- case "disable":
1300
- if (!options.hookId) {
1301
- console.log(ansis.red(i18n.t("menu:hooksSync.errors.hookIdRequired")));
1302
- return;
1303
- }
1304
- await toggleHook(options.hookId, false);
1305
- break;
1306
- case "templates":
1307
- await browseTemplates(lang, options);
1308
- break;
1309
- case "upload":
1310
- await uploadHooks();
1311
- break;
1312
- case "download":
1313
- await downloadHooks(options);
1314
- break;
1315
- default:
1316
- await showHooksMenu(lang);
1317
- }
1318
- } else {
1319
- await showHooksMenu(lang);
1320
- }
1321
- } catch (error) {
1322
- if (!handleExitPromptError(error)) {
1323
- handleGeneralError(error);
1324
- }
1325
- }
1326
- }
1327
- async function showHooksMenu(lang) {
1328
- while (true) {
1329
- console.log(ansis.green.bold(`
1330
- ${i18n.t("menu:hooksSync.title")}
1331
- `));
1332
- const { action } = await inquirer.prompt([{
1333
- type: "list",
1334
- name: "action",
1335
- message: i18n.t("menu:mcpMarket.selectAction"),
1336
- choices: [
1337
- { name: `\u{1F504} ${i18n.t("menu:hooksSync.syncNow")}`, value: "sync" },
1338
- { name: `\u{1F4CB} ${i18n.t("menu:hooksSync.viewStatus")}`, value: "list" },
1339
- { name: `\u{1F4DA} ${i18n.t("menu:hooksSync.browseTemplates")}`, value: "templates" },
1340
- { name: `\u2601\uFE0F ${i18n.t("menu:mcpMarket.upload") || "Upload"}`, value: "upload" },
1341
- { name: `\u{1F4E5} ${i18n.t("menu:mcpMarket.download") || "Download"}`, value: "download" },
1342
- new inquirer.Separator(),
1343
- { name: ansis.gray(`\u21A9\uFE0F ${i18n.t("common:back")}`), value: "back" }
1344
- ]
1345
- }]);
1346
- if (!action || action === "back") {
1347
- break;
1348
- }
1349
- switch (action) {
1350
- case "sync":
1351
- await syncHooks();
1352
- break;
1353
- case "list":
1354
- await listHooks();
1355
- break;
1356
- case "templates":
1357
- await browseTemplates(lang);
1358
- break;
1359
- case "upload":
1360
- await uploadHooks();
1361
- break;
1362
- case "download":
1363
- await downloadHooks();
1364
- break;
1365
- }
1366
- }
1367
- }
1368
- async function syncHooks() {
1369
- console.log(ansis.green(`
1370
- \u23F3 ${i18n.t("menu:hooksSync.syncNowDesc")}...`));
1371
- try {
1372
- const localHooks = await loadLocalHooks();
1373
- console.log(ansis.dim(` ${i18n.t("hooksSync:labels.localHooks")}: ${localHooks.length}`));
1374
- const client = new CloudHooksSyncClient({
1375
- enableLogging: false
1376
- });
1377
- const result = await client.syncHooks(localHooks, {
1378
- direction: "bidirectional",
1379
- overwrite: false
1380
- });
1381
- if (result.success && result.data) {
1382
- console.log(ansis.green(`
1383
- \u2705 ${i18n.t("hooksSync:actions.syncCompleted")}`));
1384
- console.log(ansis.dim(` ${i18n.t("hooksSync:labels.uploaded")}: ${result.data.uploaded}`));
1385
- console.log(ansis.dim(` ${i18n.t("hooksSync:labels.downloaded")}: ${result.data.downloaded}`));
1386
- console.log(ansis.dim(` ${i18n.t("hooksSync:labels.skipped")}: ${result.data.skipped}`));
1387
- if (result.data.failed > 0) {
1388
- console.log(ansis.yellow(` ${i18n.t("hooksSync:labels.failed")}: ${result.data.failed}`));
1389
- }
1390
- await saveLocalHooks(localHooks, result.data.timestamp);
1391
- } else {
1392
- console.log(ansis.red(`
1393
- \u274C ${i18n.t("hooksSync:actions.syncError", { error: result.error || "Unknown error" })}`));
1394
- }
1395
- } catch (error) {
1396
- console.log(ansis.red(`
1397
- \u274C ${i18n.t("hooksSync:actions.syncError", { error: error instanceof Error ? error.message : String(error) })}`));
1398
- }
1399
- }
1400
- async function uploadHooks() {
1401
- console.log(ansis.green(`
1402
- \u23F3 ${i18n.t("hooksSync:actions.uploading")}`));
1403
- try {
1404
- const localHooks = await loadLocalHooks();
1405
- if (localHooks.length === 0) {
1406
- console.log(ansis.yellow(`
1407
- \u26A0\uFE0F ${i18n.t("hooksSync:errors.noHooksFound")}`));
1408
- return;
1409
- }
1410
- console.log(ansis.dim(` ${i18n.t("hooksSync:labels.found")} ${localHooks.length} ${i18n.t("hooksSync:labels.localHooks").toLowerCase()}`));
1411
- const client = new CloudHooksSyncClient();
1412
- const result = await client.uploadHooks(localHooks);
1413
- if (result.success && result.data) {
1414
- console.log(ansis.green(`
1415
- \u2705 ${i18n.t("hooksSync:actions.uploadCompleted")}`));
1416
- console.log(ansis.dim(` ${i18n.t("hooksSync:labels.uploaded")}: ${result.data.uploaded}`));
1417
- } else {
1418
- console.log(ansis.red(`
1419
- \u274C ${i18n.t("hooksSync:actions.uploadError", { error: result.error || "Unknown error" })}`));
1420
- }
1421
- } catch (error) {
1422
- console.log(ansis.red(`
1423
- \u274C ${i18n.t("hooksSync:actions.uploadError", { error: error instanceof Error ? error.message : String(error) })}`));
1424
- }
1425
- }
1426
- async function downloadHooks(options = {}) {
1427
- console.log(ansis.green(`
1428
- \u23F3 ${i18n.t("hooksSync:actions.downloading")}`));
1429
- try {
1430
- const client = new CloudHooksSyncClient();
1431
- const result = await client.downloadHooks({
1432
- privacy: options.privacy,
1433
- category: options.category
1434
- });
1435
- if (result.success && result.data) {
1436
- console.log(ansis.green(`
1437
- \u2705 ${i18n.t("hooksSync:actions.downloadCompleted")}`));
1438
- console.log(ansis.dim(` ${i18n.t("hooksSync:labels.downloaded")}: ${result.data.length}`));
1439
- await saveLocalHooks(result.data);
1440
- result.data.forEach((hook) => {
1441
- console.log(` ${ansis.green("\u2022")} ${hook.name} ${ansis.dim(`(${hook.id})`)}`);
1442
- });
1443
- } else {
1444
- console.log(ansis.red(`
1445
- \u274C ${i18n.t("hooksSync:actions.downloadError", { error: result.error || "Unknown error" })}`));
1446
- }
1447
- } catch (error) {
1448
- console.log(ansis.red(`
1449
- \u274C ${i18n.t("hooksSync:actions.downloadError", { error: error instanceof Error ? error.message : String(error) })}`));
1450
- }
1451
- }
1452
- async function listHooks(options = {}) {
1453
- console.log(ansis.green.bold(`
1454
- \u{1F4CB} ${i18n.t("hooksSync:labels.localHooks")}
1455
- `));
1456
- try {
1457
- const hooks = await loadLocalHooks();
1458
- if (hooks.length === 0) {
1459
- console.log(ansis.yellow(` ${i18n.t("hooksSync:errors.noHooksFound")}`));
1460
- return;
1461
- }
1462
- const filteredHooks = options.category ? hooks.filter((h) => h.metadata.category === options.category) : hooks;
1463
- const byCategory = /* @__PURE__ */ new Map();
1464
- for (const hook of filteredHooks) {
1465
- const category = hook.metadata.category;
1466
- if (!byCategory.has(category)) {
1467
- byCategory.set(category, []);
1468
- }
1469
- byCategory.get(category).push(hook);
1470
- }
1471
- for (const [category, categoryHooks] of byCategory) {
1472
- console.log(ansis.bold(`
1473
- ${category}:`));
1474
- for (const hook of categoryHooks) {
1475
- const status = hook.enabled ? ansis.green("\u2713") : ansis.red("\u2717");
1476
- const privacy = hook.privacy === "public" ? ansis.green("\u{1F310}") : ansis.dim("\u{1F512}");
1477
- console.log(` ${status} ${privacy} ${hook.name} ${ansis.dim(`(${hook.id})`)}`);
1478
- console.log(` ${ansis.dim(hook.metadata.description)}`);
1479
- }
1480
- }
1481
- console.log(ansis.dim(`
1482
- ${i18n.t("hooksSync:labels.total")}: ${filteredHooks.length}`));
1483
- } catch (error) {
1484
- console.log(ansis.red(`
1485
- \u274C ${i18n.t("common:error")}: ${error instanceof Error ? error.message : String(error)}`));
1486
- }
1487
- }
1488
- async function toggleHook(hookId, enabled) {
1489
- try {
1490
- const hooks = await loadLocalHooks();
1491
- const hook = hooks.find((h) => h.id === hookId);
1492
- if (!hook) {
1493
- console.log(ansis.red(`
1494
- \u274C ${i18n.t("hooksSync:errors.hookNotFound", { id: hookId })}`));
1495
- return;
1496
- }
1497
- hook.enabled = enabled;
1498
- await saveLocalHooks(hooks);
1499
- const registry = getGlobalRegistry();
1500
- convertFromCloudHook(hook);
1501
- const entry = registry.get(hookId);
1502
- if (entry) {
1503
- if (enabled) {
1504
- registry.enable(hookId);
1505
- } else {
1506
- registry.disable(hookId);
1507
- }
1508
- }
1509
- const status = enabled ? ansis.green(i18n.t("hooksSync:labels.enabled")) : ansis.red(i18n.t("hooksSync:labels.disabled"));
1510
- console.log(`
1511
- \u2705 ${i18n.t("hooksSync:actions.hookToggled", { status, name: hook.name })}`);
1512
- } catch (error) {
1513
- console.log(ansis.red(`
1514
- \u274C ${i18n.t("common:error")}: ${error instanceof Error ? error.message : String(error)}`));
1515
- }
1516
- }
1517
- async function browseTemplates(lang, options = {}) {
1518
- console.log(ansis.green.bold(`
1519
- \u{1F4DA} ${i18n.t("menu:hooksSync.browseTemplates")}
1520
- `));
1521
- try {
1522
- const templates = hookTemplatesData.templates;
1523
- const categories = hookTemplatesData.categories;
1524
- const filteredTemplates = options.category ? templates.filter((t) => t.category === options.category) : templates;
1525
- if (filteredTemplates.length === 0) {
1526
- console.log(ansis.yellow(` ${i18n.t("hooksSync:errors.noTemplatesFound")}`));
1527
- return;
1528
- }
1529
- console.log(ansis.bold(`${i18n.t("hooksSync:labels.categories")}:`));
1530
- for (const cat of categories) {
1531
- const count = templates.filter((t) => t.category === cat.id).length;
1532
- const catName = cat.name[lang] || cat.name.en;
1533
- console.log(` ${cat.icon} ${catName} ${ansis.dim(`(${count})`)}`);
1534
- }
1535
- const { templateId } = await inquirer.prompt([{
1536
- type: "list",
1537
- name: "templateId",
1538
- message: i18n.t("hooksSync:prompts.selectTemplate"),
1539
- choices: filteredTemplates.map((t) => ({
1540
- name: `${t.name} - ${t.description}`,
1541
- value: t.id
1542
- }))
1543
- }]);
1544
- if (!templateId) {
1545
- return;
1546
- }
1547
- const template = templates.find((t) => t.id === templateId);
1548
- if (!template) {
1549
- return;
1550
- }
1551
- console.log(ansis.bold(`
1552
- ${template.name}`));
1553
- console.log(ansis.dim(template.description));
1554
- console.log(ansis.bold(`
1555
- ${i18n.t("hooksSync:labels.category")}: `) + template.category);
1556
- console.log(ansis.bold(`${i18n.t("hooksSync:labels.variables")}: `) + template.variables.join(", "));
1557
- const { confirm } = await inquirer.prompt([{
1558
- type: "confirm",
1559
- name: "confirm",
1560
- message: i18n.t("hooksSync:prompts.confirmInstall"),
1561
- default: true
1562
- }]);
1563
- if (!confirm) {
1564
- return;
1565
- }
1566
- await installTemplate(template);
1567
- } catch (error) {
1568
- console.log(ansis.red(`
1569
- \u274C ${i18n.t("common:error")}: ${error instanceof Error ? error.message : String(error)}`));
1570
- }
1571
- }
1572
- async function installTemplate(template) {
1573
- try {
1574
- const cloudHook = {
1575
- id: `template-${template.id}-${Date.now()}`,
1576
- ...template.hook,
1577
- metadata: {
1578
- author: "CCJK Templates",
1579
- description: template.description,
1580
- tags: [template.category],
1581
- category: template.category,
1582
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1583
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1584
- }
1585
- };
1586
- const hooks = await loadLocalHooks();
1587
- hooks.push(cloudHook);
1588
- await saveLocalHooks(hooks);
1589
- const registry = getGlobalRegistry();
1590
- const localHook = convertFromCloudHook(cloudHook);
1591
- registry.register(localHook);
1592
- console.log(ansis.green(`
1593
- \u2705 ${i18n.t("hooksSync:actions.templateInstalled", { name: template.name })}`));
1594
- } catch (error) {
1595
- console.log(ansis.red(`
1596
- \u274C ${i18n.t("hooksSync:actions.installError", { error: error instanceof Error ? error.message : String(error) })}`));
1597
- }
1598
- }
1599
- async function loadLocalHooks() {
1600
- try {
1601
- await ensureDir(HOOKS_DIR);
1602
- if (!await exists(HOOKS_STORAGE_FILE)) {
1603
- return [];
1604
- }
1605
- const storage = await readJsonFile(HOOKS_STORAGE_FILE);
1606
- return storage?.hooks || [];
1607
- } catch (error) {
1608
- console.error(i18n.t("hooksSync:errors.loadHooksFailed"), error);
1609
- return [];
1610
- }
1611
- }
1612
- async function saveLocalHooks(hooks, lastSyncedAt) {
1613
- try {
1614
- await ensureDir(HOOKS_DIR);
1615
- const storage = {
1616
- version: "1.0.0",
1617
- hooks,
1618
- lastSyncedAt: lastSyncedAt || (/* @__PURE__ */ new Date()).toISOString()
1619
- };
1620
- await writeFileAtomic(HOOKS_STORAGE_FILE, JSON.stringify(storage, null, 2));
1621
- } catch (error) {
1622
- console.error(i18n.t("hooksSync:errors.saveHooksFailed"), error);
1623
- throw error;
1624
- }
1625
- }
1626
-
1627
- export { hooksSync as default, hooksSync };