@hasna/skills 0.10.30 → 0.10.31
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/bin/index.js +27 -2
- package/bin/mcp.js +1 -1
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +27 -2
- package/dist/sdk/index.js +27 -2
- package/docs/plugin-admission.md +21 -0
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -36868,7 +36868,7 @@ var package_default;
|
|
|
36868
36868
|
var init_package = __esm(() => {
|
|
36869
36869
|
package_default = {
|
|
36870
36870
|
name: "@hasna/skills",
|
|
36871
|
-
version: "0.10.
|
|
36871
|
+
version: "0.10.31",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -51369,6 +51369,29 @@ function mcpControls(path, read2) {
|
|
|
51369
51369
|
refuse2();
|
|
51370
51370
|
return sha2562;
|
|
51371
51371
|
}
|
|
51372
|
+
function isBundledCleanupHooks(root, namespace, value) {
|
|
51373
|
+
const object5 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
51374
|
+
const exactKeys = (v, keys2) => Object.keys(v).length === keys2.length && keys2.every((key) => Object.hasOwn(v, key));
|
|
51375
|
+
if (basename5(dirname10(root)) !== namespace || basename5(dirname10(dirname10(root))) !== "openai-bundled")
|
|
51376
|
+
return false;
|
|
51377
|
+
const server = namespace === "unified-computer-use" ? "cua_repl" : ["browser", "chrome", "chrome-dev", "chrome-internal", "computer-use"].includes(namespace) ? "node_repl" : undefined;
|
|
51378
|
+
if (!server || !object5(value) || !exactKeys(value, ["hooks"]) || !object5(value.hooks))
|
|
51379
|
+
return false;
|
|
51380
|
+
const events = Object.entries(value.hooks);
|
|
51381
|
+
if (!events.length || events.length > 3)
|
|
51382
|
+
return false;
|
|
51383
|
+
for (const [event, groups] of events) {
|
|
51384
|
+
if (!["Interrupt", "SubagentStop", "Stop"].includes(event) || !Array.isArray(groups) || groups.length !== 1)
|
|
51385
|
+
return false;
|
|
51386
|
+
const group = groups[0];
|
|
51387
|
+
if (!object5(group) || !exactKeys(group, ["hooks"]) || !Array.isArray(group.hooks) || group.hooks.length !== 1)
|
|
51388
|
+
return false;
|
|
51389
|
+
const handler = group.hooks[0];
|
|
51390
|
+
if (!object5(handler) || !exactKeys(handler, ["type", "server", "tool", "input"]) || handler.type !== "mcp_tool" || handler.server !== server || handler.tool !== "turn_ended" || !object5(handler.input) || !exactKeys(handler.input, ["hook_event_name", "session_id", "turn_id"]) || handler.input.hook_event_name !== "${hook_event_name}" || handler.input.turn_id !== "${turn_id}" || handler.input.session_id !== (event === "SubagentStop" ? "${agent_id}" : "${session_id}"))
|
|
51391
|
+
return false;
|
|
51392
|
+
}
|
|
51393
|
+
return true;
|
|
51394
|
+
}
|
|
51372
51395
|
function rootControls(root, read2) {
|
|
51373
51396
|
const manifestPath = join18(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync12(manifestPath, { throwIfNoEntry: false });
|
|
51374
51397
|
if (!lstatSync12(join18(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
|
|
@@ -51386,9 +51409,11 @@ function rootControls(root, read2) {
|
|
|
51386
51409
|
const cacheVersion = basename5(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
51387
51410
|
if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
51388
51411
|
refuse2();
|
|
51389
|
-
if (manifest.hooks !== undefined || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync12(join18(root, "hooks"), { throwIfNoEntry: false }))
|
|
51412
|
+
if (manifest.hooks !== undefined && !isBundledCleanupHooks(root, manifest.name, manifest.hooks) || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync12(join18(root, "hooks"), { throwIfNoEntry: false }))
|
|
51390
51413
|
refuse2();
|
|
51391
51414
|
const mcp = join18(root, ".mcp.json"), mcpPresent = lstatSync12(mcp, { throwIfNoEntry: false });
|
|
51415
|
+
if (manifest.hooks !== undefined && (manifest.mcpServers !== undefined || mcpPresent))
|
|
51416
|
+
refuse2();
|
|
51392
51417
|
if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync5(mcp) !== mcp))
|
|
51393
51418
|
refuse2();
|
|
51394
51419
|
if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
|
package/bin/mcp.js
CHANGED
|
@@ -20019,7 +20019,7 @@ var package_default;
|
|
|
20019
20019
|
var init_package = __esm(() => {
|
|
20020
20020
|
package_default = {
|
|
20021
20021
|
name: "@hasna/skills",
|
|
20022
|
-
version: "0.10.
|
|
20022
|
+
version: "0.10.31",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -24122,7 +24122,7 @@ var init_dist_es9 = __esm(() => {
|
|
|
24122
24122
|
// package.json
|
|
24123
24123
|
var package_default = {
|
|
24124
24124
|
name: "@hasna/skills",
|
|
24125
|
-
version: "0.10.
|
|
24125
|
+
version: "0.10.31",
|
|
24126
24126
|
description: "Skills library for AI coding agents",
|
|
24127
24127
|
type: "module",
|
|
24128
24128
|
bin: {
|
package/bin/worker.js
CHANGED
|
@@ -24125,7 +24125,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
24125
24125
|
// package.json
|
|
24126
24126
|
var package_default = {
|
|
24127
24127
|
name: "@hasna/skills",
|
|
24128
|
-
version: "0.10.
|
|
24128
|
+
version: "0.10.31",
|
|
24129
24129
|
description: "Skills library for AI coding agents",
|
|
24130
24130
|
type: "module",
|
|
24131
24131
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -10917,7 +10917,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
10917
10917
|
// package.json
|
|
10918
10918
|
var package_default = {
|
|
10919
10919
|
name: "@hasna/skills",
|
|
10920
|
-
version: "0.10.
|
|
10920
|
+
version: "0.10.31",
|
|
10921
10921
|
description: "Skills library for AI coding agents",
|
|
10922
10922
|
type: "module",
|
|
10923
10923
|
bin: {
|
|
@@ -13509,6 +13509,29 @@ function mcpControls(path, read2) {
|
|
|
13509
13509
|
refuse2();
|
|
13510
13510
|
return sha2562;
|
|
13511
13511
|
}
|
|
13512
|
+
function isBundledCleanupHooks(root, namespace, value) {
|
|
13513
|
+
const object5 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
13514
|
+
const exactKeys = (v, keys) => Object.keys(v).length === keys.length && keys.every((key) => Object.hasOwn(v, key));
|
|
13515
|
+
if (basename5(dirname10(root)) !== namespace || basename5(dirname10(dirname10(root))) !== "openai-bundled")
|
|
13516
|
+
return false;
|
|
13517
|
+
const server = namespace === "unified-computer-use" ? "cua_repl" : ["browser", "chrome", "chrome-dev", "chrome-internal", "computer-use"].includes(namespace) ? "node_repl" : undefined;
|
|
13518
|
+
if (!server || !object5(value) || !exactKeys(value, ["hooks"]) || !object5(value.hooks))
|
|
13519
|
+
return false;
|
|
13520
|
+
const events = Object.entries(value.hooks);
|
|
13521
|
+
if (!events.length || events.length > 3)
|
|
13522
|
+
return false;
|
|
13523
|
+
for (const [event, groups] of events) {
|
|
13524
|
+
if (!["Interrupt", "SubagentStop", "Stop"].includes(event) || !Array.isArray(groups) || groups.length !== 1)
|
|
13525
|
+
return false;
|
|
13526
|
+
const group = groups[0];
|
|
13527
|
+
if (!object5(group) || !exactKeys(group, ["hooks"]) || !Array.isArray(group.hooks) || group.hooks.length !== 1)
|
|
13528
|
+
return false;
|
|
13529
|
+
const handler = group.hooks[0];
|
|
13530
|
+
if (!object5(handler) || !exactKeys(handler, ["type", "server", "tool", "input"]) || handler.type !== "mcp_tool" || handler.server !== server || handler.tool !== "turn_ended" || !object5(handler.input) || !exactKeys(handler.input, ["hook_event_name", "session_id", "turn_id"]) || handler.input.hook_event_name !== "${hook_event_name}" || handler.input.turn_id !== "${turn_id}" || handler.input.session_id !== (event === "SubagentStop" ? "${agent_id}" : "${session_id}"))
|
|
13531
|
+
return false;
|
|
13532
|
+
}
|
|
13533
|
+
return true;
|
|
13534
|
+
}
|
|
13512
13535
|
function rootControls(root, read2) {
|
|
13513
13536
|
const manifestPath = join14(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync12(manifestPath, { throwIfNoEntry: false });
|
|
13514
13537
|
if (!lstatSync12(join14(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
|
|
@@ -13526,9 +13549,11 @@ function rootControls(root, read2) {
|
|
|
13526
13549
|
const cacheVersion = basename5(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
13527
13550
|
if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
13528
13551
|
refuse2();
|
|
13529
|
-
if (manifest.hooks !== undefined || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync12(join14(root, "hooks"), { throwIfNoEntry: false }))
|
|
13552
|
+
if (manifest.hooks !== undefined && !isBundledCleanupHooks(root, manifest.name, manifest.hooks) || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync12(join14(root, "hooks"), { throwIfNoEntry: false }))
|
|
13530
13553
|
refuse2();
|
|
13531
13554
|
const mcp = join14(root, ".mcp.json"), mcpPresent = lstatSync12(mcp, { throwIfNoEntry: false });
|
|
13555
|
+
if (manifest.hooks !== undefined && (manifest.mcpServers !== undefined || mcpPresent))
|
|
13556
|
+
refuse2();
|
|
13532
13557
|
if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync5(mcp) !== mcp))
|
|
13533
13558
|
refuse2();
|
|
13534
13559
|
if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
|
package/dist/sdk/index.js
CHANGED
|
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
|
|
|
32373
32373
|
// package.json
|
|
32374
32374
|
var package_default = {
|
|
32375
32375
|
name: "@hasna/skills",
|
|
32376
|
-
version: "0.10.
|
|
32376
|
+
version: "0.10.31",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|
|
@@ -38451,6 +38451,29 @@ function mcpControls(path, read) {
|
|
|
38451
38451
|
refuse3();
|
|
38452
38452
|
return sha2562;
|
|
38453
38453
|
}
|
|
38454
|
+
function isBundledCleanupHooks(root, namespace, value) {
|
|
38455
|
+
const object3 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
|
|
38456
|
+
const exactKeys2 = (v, keys2) => Object.keys(v).length === keys2.length && keys2.every((key) => Object.hasOwn(v, key));
|
|
38457
|
+
if (basename3(dirname5(root)) !== namespace || basename3(dirname5(dirname5(root))) !== "openai-bundled")
|
|
38458
|
+
return false;
|
|
38459
|
+
const server = namespace === "unified-computer-use" ? "cua_repl" : ["browser", "chrome", "chrome-dev", "chrome-internal", "computer-use"].includes(namespace) ? "node_repl" : undefined;
|
|
38460
|
+
if (!server || !object3(value) || !exactKeys2(value, ["hooks"]) || !object3(value.hooks))
|
|
38461
|
+
return false;
|
|
38462
|
+
const events = Object.entries(value.hooks);
|
|
38463
|
+
if (!events.length || events.length > 3)
|
|
38464
|
+
return false;
|
|
38465
|
+
for (const [event, groups] of events) {
|
|
38466
|
+
if (!["Interrupt", "SubagentStop", "Stop"].includes(event) || !Array.isArray(groups) || groups.length !== 1)
|
|
38467
|
+
return false;
|
|
38468
|
+
const group = groups[0];
|
|
38469
|
+
if (!object3(group) || !exactKeys2(group, ["hooks"]) || !Array.isArray(group.hooks) || group.hooks.length !== 1)
|
|
38470
|
+
return false;
|
|
38471
|
+
const handler = group.hooks[0];
|
|
38472
|
+
if (!object3(handler) || !exactKeys2(handler, ["type", "server", "tool", "input"]) || handler.type !== "mcp_tool" || handler.server !== server || handler.tool !== "turn_ended" || !object3(handler.input) || !exactKeys2(handler.input, ["hook_event_name", "session_id", "turn_id"]) || handler.input.hook_event_name !== "${hook_event_name}" || handler.input.turn_id !== "${turn_id}" || handler.input.session_id !== (event === "SubagentStop" ? "${agent_id}" : "${session_id}"))
|
|
38473
|
+
return false;
|
|
38474
|
+
}
|
|
38475
|
+
return true;
|
|
38476
|
+
}
|
|
38454
38477
|
function rootControls(root, read) {
|
|
38455
38478
|
const manifestPath = join10(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync6(manifestPath, { throwIfNoEntry: false });
|
|
38456
38479
|
if (!lstatSync6(join10(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
|
|
@@ -38468,9 +38491,11 @@ function rootControls(root, read) {
|
|
|
38468
38491
|
const cacheVersion = basename3(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
|
|
38469
38492
|
if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
|
|
38470
38493
|
refuse3();
|
|
38471
|
-
if (manifest.hooks !== undefined || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync6(join10(root, "hooks"), { throwIfNoEntry: false }))
|
|
38494
|
+
if (manifest.hooks !== undefined && !isBundledCleanupHooks(root, manifest.name, manifest.hooks) || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync6(join10(root, "hooks"), { throwIfNoEntry: false }))
|
|
38472
38495
|
refuse3();
|
|
38473
38496
|
const mcp = join10(root, ".mcp.json"), mcpPresent = lstatSync6(mcp, { throwIfNoEntry: false });
|
|
38497
|
+
if (manifest.hooks !== undefined && (manifest.mcpServers !== undefined || mcpPresent))
|
|
38498
|
+
refuse3();
|
|
38474
38499
|
if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync5(mcp) !== mcp))
|
|
38475
38500
|
refuse3();
|
|
38476
38501
|
if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
|
package/docs/plugin-admission.md
CHANGED
|
@@ -364,3 +364,24 @@ their exact witnesses and trust state; they must still pass their own consumer
|
|
|
364
364
|
checks before use. The plan's `managedAgentChecks.agents` and `nativeSkills`
|
|
365
365
|
describe this scoped runtime check, not a fleet health result. A path-identity
|
|
366
366
|
witness intersecting the write still refuses instead of adopting a new inode.
|
|
367
|
+
|
|
368
|
+
## Codex bundled cleanup controls
|
|
369
|
+
|
|
370
|
+
Qualified native skill review supports a narrow subset of Codex 0.159.2 and
|
|
371
|
+
0.160.0's bundled cleanup hooks. The identities `browser`, `chrome`, `chrome-dev`,
|
|
372
|
+
`chrome-internal` and `computer-use` in `openai-bundled` may call
|
|
373
|
+
`node_repl.turn_ended`; `unified-computer-use` may call `cua_repl.turn_ended`.
|
|
374
|
+
Only `Interrupt`, `SubagentStop` and `Stop` are accepted, with one group and one
|
|
375
|
+
MCP handler per event. Inputs are the exact native event, turn and session
|
|
376
|
+
placeholders; `SubagentStop` uses the child `agent_id` for its session value.
|
|
377
|
+
Commands, matchers, other arguments, external hook files and a plugin MCP
|
|
378
|
+
declaration alongside these hooks remain refused. Apps cleanup routing requires
|
|
379
|
+
connector identity from a native tool catalog and is not admitted by this path.
|
|
380
|
+
|
|
381
|
+
The complete inline hooks stay in the manifest fingerprint across cache versions,
|
|
382
|
+
including after all native skill bodies disappear. A changed hook fails guarded
|
|
383
|
+
apply and subsequent discovery checks. This review preserves native cleanup;
|
|
384
|
+
it neither enables plugins nor grants hook trust, browser authority or signing.
|
|
385
|
+
The upstream contracts are
|
|
386
|
+
[0.159.2 bundled hooks](https://github.com/openai/codex/blob/ff6aec96948b70d94983af2641a6b67c94faeff5/codex-rs/plugin/src/bundled_hooks.rs)
|
|
387
|
+
and [0.160.0 bundled hooks](https://github.com/openai/codex/blob/a956835d020762cb2b570053af06f643a11c0ecc/codex-rs/plugin/src/bundled_hooks.rs).
|