@ccoalm/ccl-skills 0.18.1 → 0.18.3

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 (43) hide show
  1. package/README.md +2 -0
  2. package/dist/assets/marketplace/plugins/ccl-skills/agent-context/session-policy.md +50 -0
  3. package/dist/assets/marketplace/plugins/ccl-skills/agent-context/session-start.md +30 -40
  4. package/dist/assets/marketplace/plugins/ccl-skills/hooks/guard-delegation-owner.sh +9 -122
  5. package/dist/assets/marketplace/plugins/ccl-skills/hooks/guard-edit-isolation.sh +43 -9
  6. package/dist/assets/marketplace/plugins/ccl-skills/hooks/guard-merge-authorization.sh +95 -2
  7. package/dist/assets/marketplace/plugins/ccl-skills/hooks/hooks.json +42 -1
  8. package/dist/assets/marketplace/plugins/ccl-skills/hooks/host-input.py +560 -0
  9. package/dist/assets/marketplace/plugins/ccl-skills/hooks/merge-authorization-prompt.sh +103 -26
  10. package/dist/assets/marketplace/plugins/ccl-skills/hooks/owner-dispatch-guard.sh +17 -8
  11. package/dist/assets/marketplace/plugins/ccl-skills/hooks/proposed-next-stop.sh +14 -0
  12. package/dist/assets/marketplace/plugins/ccl-skills/hooks/session-start.sh +31 -2
  13. package/dist/assets/marketplace/plugins/ccl-skills/hooks/skill-context-compact.sh +10 -0
  14. package/dist/assets/marketplace/plugins/ccl-skills/hooks/skill-extraction-gate-stop.sh +17 -4
  15. package/dist/assets/marketplace/plugins/ccl-skills/hooks/skill-loading.py +277 -0
  16. package/dist/assets/marketplace/plugins/ccl-skills/hooks/task-entry.sh +46 -0
  17. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_guard_delegation_owner.sh +61 -55
  18. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_guard_merge_authorization.sh +140 -0
  19. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_host_input.py +630 -0
  20. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_merge_authorization_prompt.sh +56 -3
  21. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_proposed_next.py +292 -0
  22. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_session_start.sh +85 -3
  23. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_skill_loading.py +278 -0
  24. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_task_entry.py +103 -0
  25. package/dist/assets/marketplace/plugins/ccl-skills/packages/opencode-plugin/ccl-skills.ts +36 -6
  26. package/dist/assets/marketplace/plugins/ccl-skills/scripts/owner-dispatch/README.md +23 -15
  27. package/dist/assets/marketplace/plugins/ccl-skills/scripts/owner-dispatch/owner-dispatch.sh +41 -2
  28. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/SKILL.md +5 -5
  29. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/staged-review-contract.md +12 -1
  30. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/review_gate.py +26 -1
  31. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/pre-final-continuation-gate.md +9 -1
  32. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +11 -0
  33. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/extraction_review_gate.sh +2 -2
  34. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_skill_catalog.sh +5 -0
  35. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_extraction_review_gate.sh +59 -5
  36. package/dist/assets/marketplace/plugins/ccl-skills/skills/worktree-isolation/SKILL.md +1 -1
  37. package/dist/assets/marketplace/plugins/ccl-skills/skills/worktree-isolation/references/hook-authorization.md +13 -0
  38. package/dist/assets/release.json +95 -30
  39. package/dist/codex-hooks.d.ts +15 -0
  40. package/dist/codex-hooks.js +186 -0
  41. package/dist/opencode-adapter.js +8 -3
  42. package/dist/operations.js +13 -9
  43. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schema": 1,
3
3
  "npmPackage": "@ccoalm/ccl-skills",
4
- "version": "0.18.1",
5
- "sourceCommit": "40dc287d2a786256bf37242458b2ec57853171b7",
4
+ "version": "0.18.3",
5
+ "sourceCommit": "29c5c6b6d0e35572de2c269ef75b3680d0b9da35",
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": "48d6589066a033b3b24429292e18f331859f42b8fc608f07ed64244f0d02c95c",
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": "244e5f691e893f90f336bbdb401258105cb055ee03f05dd487b88241180779c0",
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": "edb2f15cd81dcecb1abf872d5e8c2ba078ce626301b898d63774a7189ac90f0d",
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": "f61d736fd61de507539584a9d7a8d4d696f7e0c56b5de096c3691b0218f1b5dc",
75
+ "sha256": "d12e8c81122babe65d12cca2576b0e2b73b40f4e82090ef31de26bbc893a1cba",
71
76
  "mode": 493
72
77
  },
73
78
  {
74
79
  "path": "marketplace/plugins/ccl-skills/hooks/hooks.json",
75
- "sha256": "a9bfda0c4f36d762a171684720745418640da90f549ce0e844fca9749b229c6d",
80
+ "sha256": "cf0749d6ed798494e622a27968207bfe3c5a465396aa261ffda6a492bc4294e2",
81
+ "mode": 420
82
+ },
83
+ {
84
+ "path": "marketplace/plugins/ccl-skills/hooks/host-input.py",
85
+ "sha256": "546abe258e25d8103f42259a58b61142418d6c4fd1696f79cfef0fe3876a90c6",
76
86
  "mode": 420
77
87
  },
78
88
  {
79
89
  "path": "marketplace/plugins/ccl-skills/hooks/merge-authorization-prompt.sh",
80
- "sha256": "295bbf8dbeab5c3241831ca9442a92cd4b60d10315aae6aa98ca99d25f14ab03",
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": "6a3aef4fff6ebe724c802fe18de79198178acd9f5fac6ab6815bd0efbc5504af",
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": "150d6a6a2b87dec330b8affaba5497ecf40483304d52c503d8ebfe6a5e51f2f8",
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": "9caf94833c361455dbd4eba7e0dc09889d78d06c4d870e0a84b07496e907156c",
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": "3e1077c0bf37d08b0697e52b1299c9694287aa5880ba92029ded33f461785100",
145
+ "sha256": "b4e5c96cd44d3dd9fdae3d6acd7ac7dc629b1ef52d85c6d42934bc84868fa8bd",
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
  {
@@ -130,9 +155,14 @@
130
155
  "sha256": "04965611f74a73030917d501efd9b205cabc52082af05e6fc9dc5be3703a8adf",
131
156
  "mode": 493
132
157
  },
158
+ {
159
+ "path": "marketplace/plugins/ccl-skills/hooks/task-entry.sh",
160
+ "sha256": "f75f318e1e1fc9ff2c2f89b3a2dec563e95d3c0a817155d8c4e78bb936ba54f9",
161
+ "mode": 493
162
+ },
133
163
  {
134
164
  "path": "marketplace/plugins/ccl-skills/hooks/test_guard_delegation_owner.sh",
135
- "sha256": "e402ea7909b54ba7d4486d6b1b8b9fc86097656f2cb7824ce66e118047c473b6",
165
+ "sha256": "0a93622a22c9b0a2dc543f77e9bc4eca892608def1cf1be2392d3dda0837bf0d",
136
166
  "mode": 420
137
167
  },
138
168
  {
@@ -142,14 +172,24 @@
142
172
  },
143
173
  {
144
174
  "path": "marketplace/plugins/ccl-skills/hooks/test_guard_merge_authorization.sh",
145
- "sha256": "c86be620e7cef11dd834ee4cdd399f753fcb784a4b4d56ca3fc1f30623bafb1b",
175
+ "sha256": "46aa31fb242e9096545d00554cb17eaf9bd4fef68fa4e49ee581148b638e3a08",
146
176
  "mode": 493
147
177
  },
178
+ {
179
+ "path": "marketplace/plugins/ccl-skills/hooks/test_host_input.py",
180
+ "sha256": "459cc71337e1d2ddcd125652fe1cb7ed04bda2c65d0d60dcae1d145b1c78dd48",
181
+ "mode": 420
182
+ },
148
183
  {
149
184
  "path": "marketplace/plugins/ccl-skills/hooks/test_merge_authorization_prompt.sh",
150
- "sha256": "4acafbec16910119c2e5cb714418aa9486881833d2694d43ec114986e9564156",
185
+ "sha256": "acb48c3fd690bd0c7353d2071e1cdd81b28a414d10452a579bf3bb044da1462d",
151
186
  "mode": 420
152
187
  },
188
+ {
189
+ "path": "marketplace/plugins/ccl-skills/hooks/test_proposed_next.py",
190
+ "sha256": "936ffb3c57162cb712b51ee7dc33a775b96ef5d7d5807424f326c62a644b8638",
191
+ "mode": 493
192
+ },
153
193
  {
154
194
  "path": "marketplace/plugins/ccl-skills/hooks/test_remind_post_merge_cleanup.sh",
155
195
  "sha256": "f57ede7e26be676f3759d97e5707c4f9390623701103f2cfc4465a9d1669abf3",
@@ -172,9 +212,19 @@
172
212
  },
173
213
  {
174
214
  "path": "marketplace/plugins/ccl-skills/hooks/test_session_start.sh",
175
- "sha256": "6f7a80cd148c93f88a7b68f6643df1420ed9a31f3bd3692e51cd7cfe1c621d0b",
215
+ "sha256": "e79bf4046ddb527a19c6599be9ede656dfe280583002706645cebfcc4ec6856b",
176
216
  "mode": 420
177
217
  },
218
+ {
219
+ "path": "marketplace/plugins/ccl-skills/hooks/test_skill_loading.py",
220
+ "sha256": "78ca5d46dc116328a8eb0552011313dafdf20c472b0bd981937ec4f9f897f462",
221
+ "mode": 493
222
+ },
223
+ {
224
+ "path": "marketplace/plugins/ccl-skills/hooks/test_task_entry.py",
225
+ "sha256": "2d7e487f12fce3a090d089a45d9296334c7213126b0925012783f2ccc29afcad",
226
+ "mode": 493
227
+ },
178
228
  {
179
229
  "path": "marketplace/plugins/ccl-skills/packages/opencode-plugin/AGENTS.md",
180
230
  "sha256": "6b0a214f91bf2abe637d2bbd430b815caf2ab4c20cd25e33a80e70f21bc02b3a",
@@ -182,7 +232,7 @@
182
232
  },
183
233
  {
184
234
  "path": "marketplace/plugins/ccl-skills/packages/opencode-plugin/ccl-skills.ts",
185
- "sha256": "4706c8345df3a68836c6e09daf9fb0d8e4a828d544b751a94c47b39029ff706c",
235
+ "sha256": "059c7309a9697eb7d5e94f83363bee9bd5cd0f1be70b12b2ae099e1f59500cb7",
186
236
  "mode": 420
187
237
  },
188
238
  {
@@ -217,12 +267,12 @@
217
267
  },
218
268
  {
219
269
  "path": "marketplace/plugins/ccl-skills/scripts/owner-dispatch/owner-dispatch.sh",
220
- "sha256": "76efb7c5d98b8944aaee11c537aabc82259faddfa6b60bb2bfabdbf7d0602ce2",
270
+ "sha256": "af57aaecf6bcb1c396d3f1072a5dc4a260807555d45755e824b082cbfd43baea",
221
271
  "mode": 493
222
272
  },
223
273
  {
224
274
  "path": "marketplace/plugins/ccl-skills/scripts/owner-dispatch/README.md",
225
- "sha256": "6e82edb997669aa82ec35c58bf5d497fac8352e91fc1ae1f0cc3f3aae90f1563",
275
+ "sha256": "00c5f07f97a24901ec97bb311cf077f5cb1d49b228449603d9f9291f36bd93af",
226
276
  "mode": 420
227
277
  },
228
278
  {
@@ -307,7 +357,7 @@
307
357
  },
308
358
  {
309
359
  "path": "marketplace/plugins/ccl-skills/skills/code-review/references/staged-review-contract.md",
310
- "sha256": "828e403ab497ce6698881287032766a9e5dfe455887d6e3b50a4f762bc4fdaf0",
360
+ "sha256": "cd54a910c1394f73af5d4eb65039c7a76617af1fd434cdc4b7b498157b41a104",
311
361
  "mode": 420
312
362
  },
313
363
  {
@@ -402,7 +452,7 @@
402
452
  },
403
453
  {
404
454
  "path": "marketplace/plugins/ccl-skills/skills/code-review/scripts/review_gate.py",
405
- "sha256": "13a548d3a09595d875f20564f351773e8d8a3f87f1ff29ddb22f0d919877c709",
455
+ "sha256": "3227b42ca37a81e557bf92d726cd84fa209d7ea6ee2947e60915b5484c45272b",
406
456
  "mode": 493
407
457
  },
408
458
  {
@@ -527,7 +577,7 @@
527
577
  },
528
578
  {
529
579
  "path": "marketplace/plugins/ccl-skills/skills/code-review/SKILL.md",
530
- "sha256": "1d6d662e424e9d754f1a9a458d27b7cee338f4ed5de1b33f73bb5eb55a30d188",
580
+ "sha256": "a87647f9ff86840d247eb5ed3b7406422fbef7f3c2da21237a331f8cb5e83781",
531
581
  "mode": 420
532
582
  },
533
583
  {
@@ -1427,7 +1477,7 @@
1427
1477
  },
1428
1478
  {
1429
1479
  "path": "marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/pre-final-continuation-gate.md",
1430
- "sha256": "084b801e8eb4c0d3ccabf102d7bc75721a00666030f3fcbc00dd74d5725ac408",
1480
+ "sha256": "498c56d47f9ced718173e2b537c03a767d01499b2556a8aa59893c2f39428630",
1431
1481
  "mode": 420
1432
1482
  },
1433
1483
  {
@@ -2157,7 +2207,7 @@
2157
2207
  },
2158
2208
  {
2159
2209
  "path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md",
2160
- "sha256": "6012341863017f917334c6906eecf533af5af481e0b88edff8f3b1849103a8a3",
2210
+ "sha256": "a7e0ec62d3670e802ca2ede7d73f796393f657d080a40a34df97c35458490bbc",
2161
2211
  "mode": 420
2162
2212
  },
2163
2213
  {
@@ -2262,7 +2312,7 @@
2262
2312
  },
2263
2313
  {
2264
2314
  "path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/extraction_review_gate.sh",
2265
- "sha256": "31793b1833026cb30ea28734340c14b72db3479c2e7485611c27ea143a75a273",
2315
+ "sha256": "247d6b9ca7996942e68a225661b43e80c1c8cb4c2d4e1042d1d201a830ba9a51",
2266
2316
  "mode": 493
2267
2317
  },
2268
2318
  {
@@ -2372,7 +2422,7 @@
2372
2422
  },
2373
2423
  {
2374
2424
  "path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_skill_catalog.sh",
2375
- "sha256": "58436a78a32aaf2bb007d342a56ca05d1eac5b47032af0d79f0d47b280c9a2a7",
2425
+ "sha256": "3c133a45044e2cc665e5e6113ad084f52f385ca82a51ddfaccaa7125d7990d2b",
2376
2426
  "mode": 493
2377
2427
  },
2378
2428
  {
@@ -2447,7 +2497,7 @@
2447
2497
  },
2448
2498
  {
2449
2499
  "path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_extraction_review_gate.sh",
2450
- "sha256": "a5ef6b42b758518b80af27cdad15ff6b731b611c7909331f8c51e925c2038fdd",
2500
+ "sha256": "0f474ac1861a1fdd8bf36c2c955c69f127b77ed1e5bd460b35d3d73290c1d15f",
2451
2501
  "mode": 493
2452
2502
  },
2453
2503
  {
@@ -3255,6 +3305,11 @@
3255
3305
  "sha256": "63ebeb661eeb644497e57a2412f4fb7bc08bb9b19fc568ecb2b6f45f4ecfb070",
3256
3306
  "mode": 420
3257
3307
  },
3308
+ {
3309
+ "path": "marketplace/plugins/ccl-skills/skills/worktree-isolation/references/hook-authorization.md",
3310
+ "sha256": "989be64bb1d59bdf92c72c4bed407381c25b906e2b436ba8ab601e5590600ce8",
3311
+ "mode": 420
3312
+ },
3258
3313
  {
3259
3314
  "path": "marketplace/plugins/ccl-skills/skills/worktree-isolation/references/shared-branch-rebase.md",
3260
3315
  "sha256": "99423dfd5acb461385de9f21f65eca802f975355a81590bec0617f401088abe8",
@@ -3287,7 +3342,7 @@
3287
3342
  },
3288
3343
  {
3289
3344
  "path": "marketplace/plugins/ccl-skills/skills/worktree-isolation/SKILL.md",
3290
- "sha256": "d5fbed89424aa1f803c6bdfc5ed9cf6772e0879ebfdaa5874b54f3ad37ee77a3",
3345
+ "sha256": "a9671ca9a82aa4f2171b9f2fc129bf1d5125317e062dc29e262d75824217e22c",
3291
3346
  "mode": 420
3292
3347
  }
3293
3348
  ],
@@ -3322,6 +3377,16 @@
3322
3377
  "sha256": "5b5ab7f39b7faa81ea209b3e479295da64f2a834969ee5ac8d76354a182c2822",
3323
3378
  "mode": 493
3324
3379
  },
3380
+ {
3381
+ "path": "codex-hooks.d.ts",
3382
+ "sha256": "c87b1afaffe146fcce3d1e8a948e67db91e7b671fd1f944b879ef4b5114a56ee",
3383
+ "mode": 420
3384
+ },
3385
+ {
3386
+ "path": "codex-hooks.js",
3387
+ "sha256": "cd2a131514391ed37e83cf8b5e90eb0d34c3f049c4d1780fa9b0443afa242de4",
3388
+ "mode": 420
3389
+ },
3325
3390
  {
3326
3391
  "path": "codex-host.d.ts",
3327
3392
  "sha256": "0e7c4e32f4627983d04d670efbd63ffe72384fb39b5ebe3bcf22a281421c7985",
@@ -3379,7 +3444,7 @@
3379
3444
  },
3380
3445
  {
3381
3446
  "path": "opencode-adapter.js",
3382
- "sha256": "637d3df5182ad6a51ed310736be65471fd0b432c078483af163d340ac04d6460",
3447
+ "sha256": "213ee1d313a8318617f3865ca261718830c497f9de79ec35f7523bd100ad9c95",
3383
3448
  "mode": 420
3384
3449
  },
3385
3450
  {
@@ -3389,7 +3454,7 @@
3389
3454
  },
3390
3455
  {
3391
3456
  "path": "operations.js",
3392
- "sha256": "254bf44226fa305e98ea2dac36ddd6ca4f1a2e42e88ee669a5bb83736ba01077",
3457
+ "sha256": "c7d2150f3158a34181c5234f02727b347c6612cdd474877d277e31f1369b31d0",
3393
3458
  "mode": 420
3394
3459
  },
3395
3460
  {
@@ -3453,5 +3518,5 @@
3453
3518
  "mode": 420
3454
3519
  }
3455
3520
  ],
3456
- "snapshotHash": "f0485aa2402d6f90224d19e22d3736178f00834e1dbd469ab1435affaed20a16"
3521
+ "snapshotHash": "1f51372beb4f07851dd24d912a7e64042ccfa3bf9a76624cca9857ddf31dcac0"
3457
3522
  }
@@ -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]);
@@ -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) => entry.source === "hooks/hooks.json" || /^hooks\/(?!test_)[^/]+\.sh$/.test(entry.source)));
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))
@@ -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
- return pending("plugin is installed; hooks trust is pending/unverified", {
489
- version: manifest.active.version,
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
- ...doctor(context),
541
- message: "same version is already installed; hooks trust remains pending/unverified",
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.1",
3
+ "version": "0.18.3",
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",