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.
- package/dist/chunks/boost.mjs +246 -7
- package/dist/chunks/ccjk-mcp.mjs +1 -1
- package/dist/chunks/ccr.mjs +25 -28
- package/dist/chunks/check-updates.mjs +4 -3
- package/dist/chunks/claude-code-config-manager.mjs +1 -1
- package/dist/chunks/claude-code-incremental-manager.mjs +1 -1
- package/dist/chunks/claude-config.mjs +1 -1
- package/dist/chunks/codex-config-switch.mjs +3 -4
- package/dist/chunks/codex-provider-manager.mjs +1 -2
- package/dist/chunks/codex.mjs +204 -3
- package/dist/chunks/config-switch.mjs +2 -3
- package/dist/chunks/config.mjs +1 -1
- package/dist/chunks/doctor.mjs +1 -1
- package/dist/chunks/features.mjs +24 -15
- package/dist/chunks/hook-installer.mjs +44 -0
- package/dist/chunks/index3.mjs +32 -32
- package/dist/chunks/init.mjs +129 -87
- package/dist/chunks/installer2.mjs +1 -1
- package/dist/chunks/interview.mjs +1 -1
- package/dist/chunks/mcp.mjs +1058 -17
- package/dist/chunks/menu.mjs +140 -56
- package/dist/chunks/package.mjs +2 -210
- package/dist/chunks/platform.mjs +1 -1
- package/dist/chunks/quick-setup.mjs +35 -18
- package/dist/chunks/simple-config.mjs +1 -1
- package/dist/{shared/ccjk.q1koQxEE.mjs → chunks/smart-defaults.mjs} +77 -79
- package/dist/chunks/status.mjs +208 -101
- package/dist/chunks/thinking.mjs +1 -1
- package/dist/chunks/uninstall.mjs +6 -4
- package/dist/chunks/update.mjs +4 -7
- package/dist/chunks/version-checker.mjs +1 -1
- package/dist/cli.mjs +4 -80
- package/dist/index.d.mts +17 -1482
- package/dist/index.d.ts +17 -1482
- package/dist/index.mjs +12 -4191
- package/dist/shared/{ccjk.CSkyCZIM.mjs → ccjk.Bndhan7G.mjs} +4 -242
- package/dist/shared/ccjk.CeE8RLG2.mjs +62 -0
- package/dist/shared/ccjk.DKojSRzw.mjs +266 -0
- package/dist/shared/{ccjk.CItD1fpl.mjs → ccjk.DvIrK0wz.mjs} +1 -1
- package/dist/shared/ccjk.LsPZ2PYo.mjs +1048 -0
- package/package.json +1 -1
- package/dist/chunks/api-adapter.mjs +0 -180
- package/dist/chunks/cli.mjs +0 -2227
- package/dist/chunks/context-menu.mjs +0 -913
- package/dist/chunks/hooks-sync.mjs +0 -1627
- package/dist/chunks/mcp-market.mjs +0 -1077
- package/dist/chunks/mcp-server.mjs +0 -776
- package/dist/chunks/project-detector.mjs +0 -131
- package/dist/chunks/provider-registry.mjs +0 -92
- package/dist/chunks/setup-wizard.mjs +0 -362
- package/dist/chunks/tools.mjs +0 -143
- package/dist/chunks/workflows2.mjs +0 -232
- package/dist/shared/ccjk.C0pb50xH.mjs +0 -347
- package/dist/shared/ccjk.ChMkBmdL.mjs +0 -490
- package/dist/shared/ccjk.CtSfXUSh.mjs +0 -209
- 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 };
|