@ccoalm/ccl-skills 0.18.1 → 0.18.2
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/README.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/agent-context/session-policy.md +50 -0
- package/dist/assets/marketplace/plugins/ccl-skills/agent-context/session-start.md +30 -40
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/guard-delegation-owner.sh +9 -122
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/guard-edit-isolation.sh +43 -9
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/guard-merge-authorization.sh +95 -2
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/hooks.json +32 -1
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/host-input.py +544 -0
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/merge-authorization-prompt.sh +103 -26
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/owner-dispatch-guard.sh +17 -8
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/proposed-next-stop.sh +14 -0
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/session-start.sh +31 -2
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/skill-context-compact.sh +10 -0
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/skill-extraction-gate-stop.sh +13 -4
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/skill-loading.py +277 -0
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_guard_delegation_owner.sh +61 -55
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_guard_merge_authorization.sh +140 -0
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_host_input.py +597 -0
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_merge_authorization_prompt.sh +56 -3
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_proposed_next.py +260 -0
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_session_start.sh +85 -3
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_skill_loading.py +278 -0
- package/dist/assets/marketplace/plugins/ccl-skills/packages/opencode-plugin/ccl-skills.ts +27 -6
- package/dist/assets/marketplace/plugins/ccl-skills/scripts/owner-dispatch/README.md +23 -15
- package/dist/assets/marketplace/plugins/ccl-skills/scripts/owner-dispatch/owner-dispatch.sh +41 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/pre-final-continuation-gate.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +4 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_skill_catalog.sh +5 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/worktree-isolation/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/worktree-isolation/references/hook-authorization.md +13 -0
- package/dist/assets/release.json +80 -25
- package/dist/codex-hooks.d.ts +15 -0
- package/dist/codex-hooks.js +186 -0
- package/dist/opencode-adapter.js +8 -3
- package/dist/operations.js +13 -9
- package/package.json +1 -1
package/dist/assets/release.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"npmPackage": "@ccoalm/ccl-skills",
|
|
4
|
-
"version": "0.18.
|
|
5
|
-
"sourceCommit": "
|
|
4
|
+
"version": "0.18.2",
|
|
5
|
+
"sourceCommit": "688ff9f11871259729fc49a5230106c2f6432539",
|
|
6
6
|
"sourceState": "clean",
|
|
7
7
|
"files": [
|
|
8
8
|
{
|
|
@@ -40,9 +40,14 @@
|
|
|
40
40
|
"sha256": "5a06ab4ac6a633be7ebb542de579a1d2153f6cf5bbe1551702a5e3d923642181",
|
|
41
41
|
"mode": 420
|
|
42
42
|
},
|
|
43
|
+
{
|
|
44
|
+
"path": "marketplace/plugins/ccl-skills/agent-context/session-policy.md",
|
|
45
|
+
"sha256": "a54a3a1bde6a3e0691995865250d2b021eeb22ccebfa5f99a31b9447325eda55",
|
|
46
|
+
"mode": 420
|
|
47
|
+
},
|
|
43
48
|
{
|
|
44
49
|
"path": "marketplace/plugins/ccl-skills/agent-context/session-start.md",
|
|
45
|
-
"sha256": "
|
|
50
|
+
"sha256": "ea0109f642fb331250e64bc28691439250575751165334f2b0175a9e3c286175",
|
|
46
51
|
"mode": 420
|
|
47
52
|
},
|
|
48
53
|
{
|
|
@@ -57,32 +62,37 @@
|
|
|
57
62
|
},
|
|
58
63
|
{
|
|
59
64
|
"path": "marketplace/plugins/ccl-skills/hooks/guard-delegation-owner.sh",
|
|
60
|
-
"sha256": "
|
|
65
|
+
"sha256": "8f5350f4a7a57edc5e9f57fb08ab211955007902e79979b6f7cc24e56e5a3695",
|
|
61
66
|
"mode": 493
|
|
62
67
|
},
|
|
63
68
|
{
|
|
64
69
|
"path": "marketplace/plugins/ccl-skills/hooks/guard-edit-isolation.sh",
|
|
65
|
-
"sha256": "
|
|
70
|
+
"sha256": "5ef7a68518bc72d1640a395db1d7c12112e24e59e6fb5b9a6dae212ba7a34272",
|
|
66
71
|
"mode": 493
|
|
67
72
|
},
|
|
68
73
|
{
|
|
69
74
|
"path": "marketplace/plugins/ccl-skills/hooks/guard-merge-authorization.sh",
|
|
70
|
-
"sha256": "
|
|
75
|
+
"sha256": "d12e8c81122babe65d12cca2576b0e2b73b40f4e82090ef31de26bbc893a1cba",
|
|
71
76
|
"mode": 493
|
|
72
77
|
},
|
|
73
78
|
{
|
|
74
79
|
"path": "marketplace/plugins/ccl-skills/hooks/hooks.json",
|
|
75
|
-
"sha256": "
|
|
80
|
+
"sha256": "5786127311b1b4969e5e10377d98f4a1f341c4e2e6fac8ab690464787ad25f09",
|
|
81
|
+
"mode": 420
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"path": "marketplace/plugins/ccl-skills/hooks/host-input.py",
|
|
85
|
+
"sha256": "129a115c9442b6cf4b0d23f657ede58b87d34a9c7913af0626b0d004080e2ba9",
|
|
76
86
|
"mode": 420
|
|
77
87
|
},
|
|
78
88
|
{
|
|
79
89
|
"path": "marketplace/plugins/ccl-skills/hooks/merge-authorization-prompt.sh",
|
|
80
|
-
"sha256": "
|
|
90
|
+
"sha256": "aba3a7fc3e6411da710dcc027f965f9042bc8ef72d74fccce0f56b527711f0a8",
|
|
81
91
|
"mode": 493
|
|
82
92
|
},
|
|
83
93
|
{
|
|
84
94
|
"path": "marketplace/plugins/ccl-skills/hooks/owner-dispatch-guard.sh",
|
|
85
|
-
"sha256": "
|
|
95
|
+
"sha256": "5d9ff755de829663f69e2f7dcf4351af9767f540a6c193458fd562eec0ee2d44",
|
|
86
96
|
"mode": 493
|
|
87
97
|
},
|
|
88
98
|
{
|
|
@@ -90,6 +100,11 @@
|
|
|
90
100
|
"sha256": "f36d6fe6c8b7c43f43301a2a3cf0f6756d4ca2e942ed8495ccd5940f2ce519bc",
|
|
91
101
|
"mode": 493
|
|
92
102
|
},
|
|
103
|
+
{
|
|
104
|
+
"path": "marketplace/plugins/ccl-skills/hooks/proposed-next-stop.sh",
|
|
105
|
+
"sha256": "b513f0e429a79f80fee8ccd7c54c14509b0f4540ce926e1893de2e93be30039f",
|
|
106
|
+
"mode": 493
|
|
107
|
+
},
|
|
93
108
|
{
|
|
94
109
|
"path": "marketplace/plugins/ccl-skills/hooks/remind-post-merge-cleanup.sh",
|
|
95
110
|
"sha256": "c912bd8bbb9afe15b4ed3ac546175ecb9185752087a818d1426ffecd68ffb623",
|
|
@@ -117,12 +132,22 @@
|
|
|
117
132
|
},
|
|
118
133
|
{
|
|
119
134
|
"path": "marketplace/plugins/ccl-skills/hooks/session-start.sh",
|
|
120
|
-
"sha256": "
|
|
135
|
+
"sha256": "fb5bb29239130df92146b3afb38384d15e0f32c0463f7c7e903683aa085d2dd6",
|
|
136
|
+
"mode": 493
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"path": "marketplace/plugins/ccl-skills/hooks/skill-context-compact.sh",
|
|
140
|
+
"sha256": "1bf21e91517d4b56a9cbe1e98bfc8f09201ee07a5f9d32441b55875df03113a8",
|
|
121
141
|
"mode": 493
|
|
122
142
|
},
|
|
123
143
|
{
|
|
124
144
|
"path": "marketplace/plugins/ccl-skills/hooks/skill-extraction-gate-stop.sh",
|
|
125
|
-
"sha256": "
|
|
145
|
+
"sha256": "aa3fa14f00c46ea7e5f12d35c3a360d9128715da6b554ccbe499794ac78dc564",
|
|
146
|
+
"mode": 493
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"path": "marketplace/plugins/ccl-skills/hooks/skill-loading.py",
|
|
150
|
+
"sha256": "f59162a353d905ef419a0075a2cf20063e7cd757799d4da7b9f7210e3ecd0272",
|
|
126
151
|
"mode": 493
|
|
127
152
|
},
|
|
128
153
|
{
|
|
@@ -132,7 +157,7 @@
|
|
|
132
157
|
},
|
|
133
158
|
{
|
|
134
159
|
"path": "marketplace/plugins/ccl-skills/hooks/test_guard_delegation_owner.sh",
|
|
135
|
-
"sha256": "
|
|
160
|
+
"sha256": "0a93622a22c9b0a2dc543f77e9bc4eca892608def1cf1be2392d3dda0837bf0d",
|
|
136
161
|
"mode": 420
|
|
137
162
|
},
|
|
138
163
|
{
|
|
@@ -142,14 +167,24 @@
|
|
|
142
167
|
},
|
|
143
168
|
{
|
|
144
169
|
"path": "marketplace/plugins/ccl-skills/hooks/test_guard_merge_authorization.sh",
|
|
145
|
-
"sha256": "
|
|
170
|
+
"sha256": "46aa31fb242e9096545d00554cb17eaf9bd4fef68fa4e49ee581148b638e3a08",
|
|
146
171
|
"mode": 493
|
|
147
172
|
},
|
|
173
|
+
{
|
|
174
|
+
"path": "marketplace/plugins/ccl-skills/hooks/test_host_input.py",
|
|
175
|
+
"sha256": "0278186bda45a9f164acad048649d7fedc558e6552d2580abd4facf5be9232eb",
|
|
176
|
+
"mode": 420
|
|
177
|
+
},
|
|
148
178
|
{
|
|
149
179
|
"path": "marketplace/plugins/ccl-skills/hooks/test_merge_authorization_prompt.sh",
|
|
150
|
-
"sha256": "
|
|
180
|
+
"sha256": "acb48c3fd690bd0c7353d2071e1cdd81b28a414d10452a579bf3bb044da1462d",
|
|
151
181
|
"mode": 420
|
|
152
182
|
},
|
|
183
|
+
{
|
|
184
|
+
"path": "marketplace/plugins/ccl-skills/hooks/test_proposed_next.py",
|
|
185
|
+
"sha256": "304bcba1cb622b4e3a14f6bf2a41eb4755352548aa791bc4f576feff6e6b129f",
|
|
186
|
+
"mode": 493
|
|
187
|
+
},
|
|
153
188
|
{
|
|
154
189
|
"path": "marketplace/plugins/ccl-skills/hooks/test_remind_post_merge_cleanup.sh",
|
|
155
190
|
"sha256": "f57ede7e26be676f3759d97e5707c4f9390623701103f2cfc4465a9d1669abf3",
|
|
@@ -172,9 +207,14 @@
|
|
|
172
207
|
},
|
|
173
208
|
{
|
|
174
209
|
"path": "marketplace/plugins/ccl-skills/hooks/test_session_start.sh",
|
|
175
|
-
"sha256": "
|
|
210
|
+
"sha256": "e79bf4046ddb527a19c6599be9ede656dfe280583002706645cebfcc4ec6856b",
|
|
176
211
|
"mode": 420
|
|
177
212
|
},
|
|
213
|
+
{
|
|
214
|
+
"path": "marketplace/plugins/ccl-skills/hooks/test_skill_loading.py",
|
|
215
|
+
"sha256": "78ca5d46dc116328a8eb0552011313dafdf20c472b0bd981937ec4f9f897f462",
|
|
216
|
+
"mode": 493
|
|
217
|
+
},
|
|
178
218
|
{
|
|
179
219
|
"path": "marketplace/plugins/ccl-skills/packages/opencode-plugin/AGENTS.md",
|
|
180
220
|
"sha256": "6b0a214f91bf2abe637d2bbd430b815caf2ab4c20cd25e33a80e70f21bc02b3a",
|
|
@@ -182,7 +222,7 @@
|
|
|
182
222
|
},
|
|
183
223
|
{
|
|
184
224
|
"path": "marketplace/plugins/ccl-skills/packages/opencode-plugin/ccl-skills.ts",
|
|
185
|
-
"sha256": "
|
|
225
|
+
"sha256": "b5c6c3d29f3b8406cee9ef2de2f9f364720eccbcec5e5d313aea044546aaa07f",
|
|
186
226
|
"mode": 420
|
|
187
227
|
},
|
|
188
228
|
{
|
|
@@ -217,12 +257,12 @@
|
|
|
217
257
|
},
|
|
218
258
|
{
|
|
219
259
|
"path": "marketplace/plugins/ccl-skills/scripts/owner-dispatch/owner-dispatch.sh",
|
|
220
|
-
"sha256": "
|
|
260
|
+
"sha256": "af57aaecf6bcb1c396d3f1072a5dc4a260807555d45755e824b082cbfd43baea",
|
|
221
261
|
"mode": 493
|
|
222
262
|
},
|
|
223
263
|
{
|
|
224
264
|
"path": "marketplace/plugins/ccl-skills/scripts/owner-dispatch/README.md",
|
|
225
|
-
"sha256": "
|
|
265
|
+
"sha256": "00c5f07f97a24901ec97bb311cf077f5cb1d49b228449603d9f9291f36bd93af",
|
|
226
266
|
"mode": 420
|
|
227
267
|
},
|
|
228
268
|
{
|
|
@@ -1427,7 +1467,7 @@
|
|
|
1427
1467
|
},
|
|
1428
1468
|
{
|
|
1429
1469
|
"path": "marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/pre-final-continuation-gate.md",
|
|
1430
|
-
"sha256": "
|
|
1470
|
+
"sha256": "f1a34ebdf9be63381c20d592f56c7bd1983421d27fcb01489b3e166de5aaf97a",
|
|
1431
1471
|
"mode": 420
|
|
1432
1472
|
},
|
|
1433
1473
|
{
|
|
@@ -2157,7 +2197,7 @@
|
|
|
2157
2197
|
},
|
|
2158
2198
|
{
|
|
2159
2199
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md",
|
|
2160
|
-
"sha256": "
|
|
2200
|
+
"sha256": "fa473d3b861716e0f8369a3f5583e8c57896f736104194bb854174e7bacd8009",
|
|
2161
2201
|
"mode": 420
|
|
2162
2202
|
},
|
|
2163
2203
|
{
|
|
@@ -2372,7 +2412,7 @@
|
|
|
2372
2412
|
},
|
|
2373
2413
|
{
|
|
2374
2414
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_skill_catalog.sh",
|
|
2375
|
-
"sha256": "
|
|
2415
|
+
"sha256": "3c133a45044e2cc665e5e6113ad084f52f385ca82a51ddfaccaa7125d7990d2b",
|
|
2376
2416
|
"mode": 493
|
|
2377
2417
|
},
|
|
2378
2418
|
{
|
|
@@ -3255,6 +3295,11 @@
|
|
|
3255
3295
|
"sha256": "63ebeb661eeb644497e57a2412f4fb7bc08bb9b19fc568ecb2b6f45f4ecfb070",
|
|
3256
3296
|
"mode": 420
|
|
3257
3297
|
},
|
|
3298
|
+
{
|
|
3299
|
+
"path": "marketplace/plugins/ccl-skills/skills/worktree-isolation/references/hook-authorization.md",
|
|
3300
|
+
"sha256": "989be64bb1d59bdf92c72c4bed407381c25b906e2b436ba8ab601e5590600ce8",
|
|
3301
|
+
"mode": 420
|
|
3302
|
+
},
|
|
3258
3303
|
{
|
|
3259
3304
|
"path": "marketplace/plugins/ccl-skills/skills/worktree-isolation/references/shared-branch-rebase.md",
|
|
3260
3305
|
"sha256": "99423dfd5acb461385de9f21f65eca802f975355a81590bec0617f401088abe8",
|
|
@@ -3287,7 +3332,7 @@
|
|
|
3287
3332
|
},
|
|
3288
3333
|
{
|
|
3289
3334
|
"path": "marketplace/plugins/ccl-skills/skills/worktree-isolation/SKILL.md",
|
|
3290
|
-
"sha256": "
|
|
3335
|
+
"sha256": "a9671ca9a82aa4f2171b9f2fc129bf1d5125317e062dc29e262d75824217e22c",
|
|
3291
3336
|
"mode": 420
|
|
3292
3337
|
}
|
|
3293
3338
|
],
|
|
@@ -3322,6 +3367,16 @@
|
|
|
3322
3367
|
"sha256": "5b5ab7f39b7faa81ea209b3e479295da64f2a834969ee5ac8d76354a182c2822",
|
|
3323
3368
|
"mode": 493
|
|
3324
3369
|
},
|
|
3370
|
+
{
|
|
3371
|
+
"path": "codex-hooks.d.ts",
|
|
3372
|
+
"sha256": "c87b1afaffe146fcce3d1e8a948e67db91e7b671fd1f944b879ef4b5114a56ee",
|
|
3373
|
+
"mode": 420
|
|
3374
|
+
},
|
|
3375
|
+
{
|
|
3376
|
+
"path": "codex-hooks.js",
|
|
3377
|
+
"sha256": "cd2a131514391ed37e83cf8b5e90eb0d34c3f049c4d1780fa9b0443afa242de4",
|
|
3378
|
+
"mode": 420
|
|
3379
|
+
},
|
|
3325
3380
|
{
|
|
3326
3381
|
"path": "codex-host.d.ts",
|
|
3327
3382
|
"sha256": "0e7c4e32f4627983d04d670efbd63ffe72384fb39b5ebe3bcf22a281421c7985",
|
|
@@ -3379,7 +3434,7 @@
|
|
|
3379
3434
|
},
|
|
3380
3435
|
{
|
|
3381
3436
|
"path": "opencode-adapter.js",
|
|
3382
|
-
"sha256": "
|
|
3437
|
+
"sha256": "213ee1d313a8318617f3865ca261718830c497f9de79ec35f7523bd100ad9c95",
|
|
3383
3438
|
"mode": 420
|
|
3384
3439
|
},
|
|
3385
3440
|
{
|
|
@@ -3389,7 +3444,7 @@
|
|
|
3389
3444
|
},
|
|
3390
3445
|
{
|
|
3391
3446
|
"path": "operations.js",
|
|
3392
|
-
"sha256": "
|
|
3447
|
+
"sha256": "c7d2150f3158a34181c5234f02727b347c6612cdd474877d277e31f1369b31d0",
|
|
3393
3448
|
"mode": 420
|
|
3394
3449
|
},
|
|
3395
3450
|
{
|
|
@@ -3453,5 +3508,5 @@
|
|
|
3453
3508
|
"mode": 420
|
|
3454
3509
|
}
|
|
3455
3510
|
],
|
|
3456
|
-
"snapshotHash": "
|
|
3511
|
+
"snapshotHash": "d0da3b335908383d4b8fd193d5755f978933a5e430e93eb48f49914669ad3491"
|
|
3457
3512
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { OwnedFile } from "./types.js";
|
|
2
|
+
type HookStatus = "trusted" | "managed" | "untrusted" | "modified" | "disabled" | "missing" | "unknown";
|
|
3
|
+
export interface HookInventory {
|
|
4
|
+
status: HookStatus;
|
|
5
|
+
expected: number;
|
|
6
|
+
observed: number;
|
|
7
|
+
runtime: "unverified";
|
|
8
|
+
reason?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function parseHookInventory(value: unknown, cwd: string, pluginRoot: string, runtime?: {
|
|
11
|
+
codexHome: string;
|
|
12
|
+
files: OwnedFile[];
|
|
13
|
+
}): HookInventory;
|
|
14
|
+
export declare function inspectHooks(codexHome: string, pluginRoot: string, files: OwnedFile[], cwd?: string): HookInventory;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { canonicalAlias, contained, verifyTree } from "./fs-safe.js";
|
|
6
|
+
import { MARKET, REF } from "./paths.js";
|
|
7
|
+
const TIMEOUT_MS = 10000;
|
|
8
|
+
const OUTPUT_LIMIT = 1024 * 1024;
|
|
9
|
+
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10
|
+
const unknown = (reason, expected = 0, observed = 0) => ({ status: "unknown", expected, observed, runtime: "unverified", reason });
|
|
11
|
+
// Compare native metadata with the verified package snapshot. Merely finding a
|
|
12
|
+
// similarly named plugin or one trusted handler cannot certify the installation.
|
|
13
|
+
export function parseHookInventory(value, cwd, pluginRoot, runtime) {
|
|
14
|
+
let expected = [];
|
|
15
|
+
try {
|
|
16
|
+
const config = JSON.parse(readFileSync(join(pluginRoot, "hooks/hooks.json"), "utf8"));
|
|
17
|
+
if (!record(config) || !record(config.hooks))
|
|
18
|
+
return unknown("invalid-owned-hooks");
|
|
19
|
+
for (const [event, groups] of Object.entries(config.hooks)) {
|
|
20
|
+
if (!Array.isArray(groups))
|
|
21
|
+
return unknown("invalid-owned-hooks");
|
|
22
|
+
for (const group of groups) {
|
|
23
|
+
if (!record(group) || !Array.isArray(group.hooks) || (group.matcher !== undefined && typeof group.matcher !== "string"))
|
|
24
|
+
return unknown("invalid-owned-hooks");
|
|
25
|
+
for (const hook of group.hooks) {
|
|
26
|
+
if (!record(hook) || hook.type !== "command" || typeof hook.command !== "string" || !hook.command)
|
|
27
|
+
return unknown("invalid-owned-hooks");
|
|
28
|
+
expected.push({ eventName: event[0].toLowerCase() + event.slice(1), matcher: group.matcher ?? null, command: hook.command });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return unknown("invalid-owned-hooks");
|
|
35
|
+
}
|
|
36
|
+
if (!expected.length)
|
|
37
|
+
return unknown("empty-owned-hooks");
|
|
38
|
+
const count = expected.length;
|
|
39
|
+
if (!record(value) || !Array.isArray(value.data) || value.data.length !== 1)
|
|
40
|
+
return unknown("malformed-inventory", count);
|
|
41
|
+
const entry = value.data[0];
|
|
42
|
+
if (!record(entry) || typeof entry.cwd !== "string" || !isAbsolute(entry.cwd) || canonicalAlias(resolve(entry.cwd)) !== canonicalAlias(resolve(cwd)) || !Array.isArray(entry.hooks))
|
|
43
|
+
return unknown("malformed-inventory", count);
|
|
44
|
+
if (!Array.isArray(entry.errors) || !Array.isArray(entry.warnings) || entry.errors.length || entry.warnings.length)
|
|
45
|
+
return unknown("inventory-diagnostics", count);
|
|
46
|
+
const statuses = [], keys = new Set();
|
|
47
|
+
let disabled = false;
|
|
48
|
+
const expectedSource = canonicalAlias(join(pluginRoot, "hooks/hooks.json"));
|
|
49
|
+
const verifiedRoots = new Set([canonicalAlias(pluginRoot)]);
|
|
50
|
+
for (const hook of entry.hooks) {
|
|
51
|
+
if (!record(hook))
|
|
52
|
+
return unknown("malformed-inventory", count);
|
|
53
|
+
if (hook.pluginId !== REF)
|
|
54
|
+
continue;
|
|
55
|
+
if (hook.source !== "plugin" || typeof hook.sourcePath !== "string" || !isAbsolute(hook.sourcePath))
|
|
56
|
+
return unknown("hook-source-mismatch", count, statuses.length);
|
|
57
|
+
const source = canonicalAlias(resolve(hook.sourcePath)), root = dirname(dirname(source));
|
|
58
|
+
if (source !== expectedSource) {
|
|
59
|
+
if (!runtime || source !== join(root, "hooks/hooks.json"))
|
|
60
|
+
return unknown("hook-source-mismatch", count, statuses.length);
|
|
61
|
+
const cache = canonicalAlias(join(runtime.codexHome, "plugins/cache", MARKET, "ccl-skills"));
|
|
62
|
+
try {
|
|
63
|
+
if (root === cache || contained(cache, relative(cache, root)) !== root)
|
|
64
|
+
return unknown("hook-source-mismatch", count, statuses.length);
|
|
65
|
+
if (!verifiedRoots.has(root)) {
|
|
66
|
+
if (!runtime.files.length || verifyTree(root, runtime.files).length)
|
|
67
|
+
return unknown("runtime-drift", count, statuses.length);
|
|
68
|
+
verifiedRoots.add(root);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return unknown("hook-source-mismatch", count, statuses.length);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (hook.handlerType !== "command" || typeof hook.command !== "string" || typeof hook.eventName !== "string" || (hook.matcher !== null && typeof hook.matcher !== "string") || typeof hook.enabled !== "boolean" || typeof hook.key !== "string" || !hook.key || keys.has(hook.key) || !["trusted", "untrusted", "modified", "managed"].includes(String(hook.trustStatus)))
|
|
76
|
+
return unknown("malformed-hook", count, statuses.length);
|
|
77
|
+
keys.add(hook.key);
|
|
78
|
+
const commandRoot = dirname(dirname(resolve(hook.sourcePath)));
|
|
79
|
+
const index = expected.findIndex(item => item.eventName === hook.eventName && item.matcher === hook.matcher && item.command.replaceAll("${CLAUDE_PLUGIN_ROOT}", commandRoot).replaceAll("${CODEX_PLUGIN_ROOT}", commandRoot) === hook.command);
|
|
80
|
+
if (index < 0)
|
|
81
|
+
return unknown("hook-definition-mismatch", count, statuses.length);
|
|
82
|
+
expected.splice(index, 1);
|
|
83
|
+
statuses.push(String(hook.trustStatus));
|
|
84
|
+
disabled ||= !hook.enabled;
|
|
85
|
+
}
|
|
86
|
+
const base = { expected: count, observed: statuses.length, runtime: "unverified" };
|
|
87
|
+
if (expected.length)
|
|
88
|
+
return { ...base, status: "missing", reason: "missing-package-hooks" };
|
|
89
|
+
if (statuses.includes("modified"))
|
|
90
|
+
return { ...base, status: "modified" };
|
|
91
|
+
if (statuses.includes("untrusted"))
|
|
92
|
+
return { ...base, status: "untrusted" };
|
|
93
|
+
if (disabled)
|
|
94
|
+
return { ...base, status: "disabled" };
|
|
95
|
+
return { ...base, status: statuses.every(status => status === "managed") ? "managed" : "trusted" };
|
|
96
|
+
}
|
|
97
|
+
// The CLI's operation API is synchronous. Isolate the handshake in a short-lived
|
|
98
|
+
// helper so initialization completes before hooks/list, without starting a turn.
|
|
99
|
+
export function inspectHooks(codexHome, pluginRoot, files, cwd = process.cwd()) {
|
|
100
|
+
const result = spawnSync(process.execPath, [fileURLToPath(import.meta.url), "--inventory-worker", resolve(cwd)], {
|
|
101
|
+
encoding: "utf8", env: { ...process.env, CODEX_HOME: codexHome },
|
|
102
|
+
stdio: ["ignore", "pipe", "pipe"], detached: process.platform !== "win32",
|
|
103
|
+
timeout: TIMEOUT_MS + 1000, killSignal: "SIGKILL", maxBuffer: OUTPUT_LIMIT,
|
|
104
|
+
});
|
|
105
|
+
// The helper and its descendants share an isolated process group. This also
|
|
106
|
+
// catches hosts which leave children alive or hold stdout open after exit.
|
|
107
|
+
if (process.platform !== "win32" && result.pid) {
|
|
108
|
+
try {
|
|
109
|
+
process.kill(-result.pid, "SIGKILL");
|
|
110
|
+
}
|
|
111
|
+
catch { /* group already gone */ }
|
|
112
|
+
}
|
|
113
|
+
if (result.error || result.status !== 0)
|
|
114
|
+
return unknown(result.error?.code === "ETIMEDOUT" ? "timeout" : "unavailable");
|
|
115
|
+
try {
|
|
116
|
+
const payload = JSON.parse(result.stdout);
|
|
117
|
+
if (!record(payload) || payload.ok !== true)
|
|
118
|
+
return unknown(record(payload) && typeof payload.reason === "string" && ["timeout", "unavailable", "malformed-output", "output-limit", "unsupported"].includes(payload.reason) ? payload.reason : "malformed-output");
|
|
119
|
+
return parseHookInventory(payload.result, cwd, pluginRoot, { codexHome, files });
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return unknown("malformed-output");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function inventoryWorker(cwd) {
|
|
126
|
+
const child = spawn("codex", ["app-server", "--stdio"], { stdio: ["pipe", "pipe", "pipe"] });
|
|
127
|
+
let finished = false, initialized = false, received = 0, pending = "";
|
|
128
|
+
const timer = setTimeout(() => finish({ ok: false, reason: "timeout" }), TIMEOUT_MS);
|
|
129
|
+
function finish(value) {
|
|
130
|
+
if (finished)
|
|
131
|
+
return;
|
|
132
|
+
finished = true;
|
|
133
|
+
clearTimeout(timer);
|
|
134
|
+
child.kill("SIGKILL");
|
|
135
|
+
child.stdin.destroy();
|
|
136
|
+
child.stdout.destroy();
|
|
137
|
+
child.stderr.destroy();
|
|
138
|
+
process.stdout.write(JSON.stringify(value));
|
|
139
|
+
}
|
|
140
|
+
const send = (value) => child.stdin.write(`${JSON.stringify(value)}\n`);
|
|
141
|
+
child.on("error", () => finish({ ok: false, reason: "unavailable" }));
|
|
142
|
+
child.on("exit", () => finish({ ok: false, reason: "unavailable" }));
|
|
143
|
+
child.stdin.on("error", () => finish({ ok: false, reason: "unavailable" }));
|
|
144
|
+
child.stderr.on("data", (chunk) => {
|
|
145
|
+
received += chunk.length;
|
|
146
|
+
if (received > OUTPUT_LIMIT)
|
|
147
|
+
finish({ ok: false, reason: "output-limit" });
|
|
148
|
+
});
|
|
149
|
+
child.stdout.setEncoding("utf8");
|
|
150
|
+
child.stdout.on("data", (chunk) => {
|
|
151
|
+
received += Buffer.byteLength(chunk, "utf8");
|
|
152
|
+
if (received > OUTPUT_LIMIT)
|
|
153
|
+
return finish({ ok: false, reason: "output-limit" });
|
|
154
|
+
pending += chunk;
|
|
155
|
+
let newline;
|
|
156
|
+
while (!finished && (newline = pending.indexOf("\n")) >= 0) {
|
|
157
|
+
const line = pending.slice(0, newline);
|
|
158
|
+
pending = pending.slice(newline + 1);
|
|
159
|
+
let response;
|
|
160
|
+
try {
|
|
161
|
+
response = JSON.parse(line);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return finish({ ok: false, reason: "malformed-output" });
|
|
165
|
+
}
|
|
166
|
+
if (!record(response))
|
|
167
|
+
return finish({ ok: false, reason: "malformed-output" });
|
|
168
|
+
if (!Object.hasOwn(response, "id") && typeof response.method === "string")
|
|
169
|
+
continue;
|
|
170
|
+
if (response.id !== (initialized ? 2 : 1))
|
|
171
|
+
return finish({ ok: false, reason: "malformed-output" });
|
|
172
|
+
if (Object.hasOwn(response, "error"))
|
|
173
|
+
return finish({ ok: false, reason: "unsupported" });
|
|
174
|
+
if (!record(response.result))
|
|
175
|
+
return finish({ ok: false, reason: "malformed-output" });
|
|
176
|
+
if (initialized)
|
|
177
|
+
return finish({ ok: true, result: response.result });
|
|
178
|
+
initialized = true;
|
|
179
|
+
send({ method: "initialized", params: {} });
|
|
180
|
+
send({ id: 2, method: "hooks/list", params: { cwds: [cwd] } });
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
send({ id: 1, method: "initialize", params: { clientInfo: { name: "ccl_hook_audit", version: "1.0" }, capabilities: { experimentalApi: true } } });
|
|
184
|
+
}
|
|
185
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url) && process.argv[2] === "--inventory-worker" && isAbsolute(process.argv[3] ?? ""))
|
|
186
|
+
inventoryWorker(process.argv[3]);
|
package/dist/opencode-adapter.js
CHANGED
|
@@ -69,16 +69,21 @@ function source(context) {
|
|
|
69
69
|
throw new Error("invalid CCL_SKILLS_REPO: skills, OpenCode commands, or hook runtime are missing");
|
|
70
70
|
regular(plugin);
|
|
71
71
|
regular(bootstrap);
|
|
72
|
+
for (const helper of ["host-input.py", "skill-loading.py"])
|
|
73
|
+
regular(join(hooks, helper));
|
|
72
74
|
const skillNames = readdirSync(skills, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync(join(skills, entry.name, "SKILL.md"))).map((entry) => entry.name).sort();
|
|
73
75
|
if (!skillNames.length)
|
|
74
76
|
throw new Error("invalid CCL_SKILLS_REPO: no skills found");
|
|
75
77
|
let entries = walk(skills, "skills", "skills");
|
|
76
78
|
entries.push({ source: "packages/opencode-plugin/ccl-skills.ts", destination: "plugins/ccl-skills.ts", sha256: sha256(plugin), mode: regular(plugin).mode & 0o777 });
|
|
77
79
|
entries.push({ source: "agent-context/session-start.md", destination: "ccl-skills/bootstrap.md", sha256: sha256(bootstrap), mode: regular(bootstrap).mode & 0o777 });
|
|
80
|
+
const policy = join(agentContext, "session-policy.md");
|
|
81
|
+
regular(policy);
|
|
82
|
+
entries.push({ source: "agent-context/session-policy.md", destination: "ccl-skills/session-policy.md", sha256: sha256(policy), mode: regular(policy).mode & 0o777 });
|
|
78
83
|
entries.push(...walk(commands, "packages/opencode-plugin/commands", "commands").filter((entry) => /^commands\/ccl-[^/]+\.md$/.test(entry.destination)));
|
|
79
|
-
entries.push(...walk(hooks, "hooks", "ccl-skills/runtime/hooks").filter((entry) =>
|
|
84
|
+
entries.push(...walk(hooks, "hooks", "ccl-skills/runtime/hooks").filter((entry) => ["hooks/hooks.json", "hooks/host-input.py", "hooks/skill-loading.py"].includes(entry.source) || /^hooks\/(?!test_)[^/]+\.sh$/.test(entry.source)));
|
|
80
85
|
entries.push(...walk(ownerDispatch, "scripts/owner-dispatch", "ccl-skills/runtime/scripts/owner-dispatch").filter((entry) => entry.source === "scripts/owner-dispatch/owner-dispatch.sh"));
|
|
81
|
-
entries.push(...walk(agentContext, "agent-context", "ccl-skills/runtime/agent-context").filter((entry) => /^agent-context\/(session-start|subagent-start)\.md$/.test(entry.source)));
|
|
86
|
+
entries.push(...walk(agentContext, "agent-context", "ccl-skills/runtime/agent-context").filter((entry) => /^agent-context\/(session-start|session-policy|subagent-start)\.md$/.test(entry.source)));
|
|
82
87
|
entries = entries.sort((a, b) => a.destination.localeCompare(b.destination));
|
|
83
88
|
const snapshotHash = createHash("sha256").update(JSON.stringify(entries.map(({ source: s, sha256: hash, mode }) => ({ source: s, sha256: hash, mode })))).digest("hex");
|
|
84
89
|
const release = readRelease(join(p.assets, "release.json"));
|
|
@@ -90,7 +95,7 @@ function source(context) {
|
|
|
90
95
|
return { root, entries, snapshotHash, version: release.version, sourceCommit, sourceKind: override ? "override" : "bundled" };
|
|
91
96
|
}
|
|
92
97
|
function allowedDestination(path) {
|
|
93
|
-
return /^skills\/[^/]+\/.+/.test(path) || path === "plugins/ccl-skills.ts" || path === "ccl-skills/bootstrap.md" || /^commands\/ccl-[^/]+\.md$/.test(path) || /^ccl-skills\/runtime\/(hooks|scripts\/owner-dispatch|agent-context)\/[^/]+$/.test(path);
|
|
98
|
+
return /^skills\/[^/]+\/.+/.test(path) || path === "plugins/ccl-skills.ts" || path === "ccl-skills/bootstrap.md" || path === "ccl-skills/session-policy.md" || /^commands\/ccl-[^/]+\.md$/.test(path) || /^ccl-skills\/runtime\/(hooks|scripts\/owner-dispatch|agent-context)\/[^/]+$/.test(path);
|
|
94
99
|
}
|
|
95
100
|
function readManifest(path, root) {
|
|
96
101
|
if (!existsSync(path))
|
package/dist/operations.js
CHANGED
|
@@ -5,6 +5,7 @@ import { checkHost, marketplaceAdd, marketplaceRemove, pluginAdd, pluginRemove,
|
|
|
5
5
|
import { manifestFor, MetadataError, readManifest, readRelease, } from "./manifest.js";
|
|
6
6
|
import { paths, } from "./paths.js";
|
|
7
7
|
import { compare } from "./version.js";
|
|
8
|
+
import { inspectHooks } from "./codex-hooks.js";
|
|
8
9
|
const pending = (message, details = {}) => ({
|
|
9
10
|
code: 3,
|
|
10
11
|
status: "installed-hooks-pending",
|
|
@@ -485,9 +486,13 @@ function doctor(context) {
|
|
|
485
486
|
status: "public-state-mismatch",
|
|
486
487
|
message: "owned files exist but Codex registration does not match the manifest",
|
|
487
488
|
};
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
489
|
+
const pluginPrefix = "marketplace/plugins/ccl-skills/";
|
|
490
|
+
const pluginFiles = manifest.active.ownedFiles.filter(file => file.path.startsWith(pluginPrefix)).map(file => ({ ...file, path: file.path.slice(pluginPrefix.length) }));
|
|
491
|
+
const hooks = inspectHooks(p.codexHome, join(marketPath(p.root, manifest.active.path), "plugins/ccl-skills"), pluginFiles);
|
|
492
|
+
const details = { version: manifest.active.version, hooks };
|
|
493
|
+
if (hooks.status === "trusted" || hooks.status === "managed")
|
|
494
|
+
return { code: 0, status: "installed-hooks-trusted", message: "plugin hooks are configured and trusted; runtime execution is not verified", details };
|
|
495
|
+
return pending(`plugin is installed; hook trust: ${hooks.status}; runtime execution is not verified`, details);
|
|
491
496
|
}
|
|
492
497
|
function installOrUpdate(command, options, context) {
|
|
493
498
|
const p = paths(context);
|
|
@@ -534,13 +539,12 @@ function installOrUpdate(command, options, context) {
|
|
|
534
539
|
message: "public state does not match the owned manifest",
|
|
535
540
|
};
|
|
536
541
|
const comparison = compare(release.version, old.active.version);
|
|
537
|
-
if (comparison === 0)
|
|
542
|
+
if (comparison === 0) {
|
|
543
|
+
const report = doctor(context);
|
|
538
544
|
return command === "install"
|
|
539
|
-
? {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
: doctor(context);
|
|
545
|
+
? { ...report, message: `same version is already installed; ${report.message}` }
|
|
546
|
+
: report;
|
|
547
|
+
}
|
|
544
548
|
if (command === "install")
|
|
545
549
|
return {
|
|
546
550
|
code: 3,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ccoalm/ccl-skills",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"description": "Reusable workflows that help coding agents plan, build, test, review, and release software — for Claude Code, Codex, and OpenCode",
|
|
5
5
|
"keywords": ["skills", "agent-skills", "claude", "claude-code", "codex", "opencode", "agent", "ai", "ai-agents", "cli", "anthropic", "developer-tools"],
|
|
6
6
|
"type": "module",
|