@hasna/skills 0.10.22 → 0.10.23

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 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.22",
36871
+ version: "0.10.23",
36872
36872
  description: "Skills library for AI coding agents",
36873
36873
  type: "module",
36874
36874
  bin: {
@@ -39931,7 +39931,7 @@ function assertAgentPolicyCollections(policy) {
39931
39931
  }
39932
39932
  if (bridge.codexPluginSkills !== undefined)
39933
39933
  for (const control of array(bridge.codexPluginSkills, 4096)) {
39934
- requireBound(object(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "remotePluginId"].includes(key)));
39934
+ requireBound(object(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "mcpSha256", "remotePluginId"].includes(key)));
39935
39935
  text(control.name, 129);
39936
39936
  text(control.pluginId, 1024);
39937
39937
  text(control.namespace, 64);
@@ -39939,6 +39939,8 @@ function assertAgentPolicyCollections(policy) {
39939
39939
  requireBound(isAbsolute2(control.pluginParent) && resolve3(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
39940
39940
  if (control.remotePluginId !== undefined)
39941
39941
  requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
39942
+ if (control.mcpSha256 !== undefined)
39943
+ requireBound(typeof control.mcpSha256 === "string" && /^[a-f0-9]{64}$/.test(control.mcpSha256));
39942
39944
  if (control.appSha256 !== undefined)
39943
39945
  requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
39944
39946
  }
@@ -51226,6 +51228,42 @@ function appControls(path, read2) {
51226
51228
  }
51227
51229
  return sha2562;
51228
51230
  }
51231
+ function mcpControls(path, read2) {
51232
+ const text2 = read2(path);
51233
+ let value, sha2562;
51234
+ try {
51235
+ sha2562 = hashNativeJsonControls(text2);
51236
+ value = JSON.parse(text2);
51237
+ } catch {
51238
+ refuse2();
51239
+ }
51240
+ const object5 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
51241
+ const textField = (v, empty = false) => typeof v === "string" && (empty || Boolean(v.trim())) && Buffer.byteLength(v) <= 16384 && !/[\x00-\x1f\x7f]/.test(v);
51242
+ const envName = (v) => typeof v === "string" && /^[A-Za-z_][A-Za-z0-9_]{0,255}$/.test(v);
51243
+ if (!object5(value) || Object.keys(value).length !== 1 || !object5(value.mcpServers))
51244
+ refuse2();
51245
+ const servers = Object.entries(value.mcpServers);
51246
+ if (!servers.length || servers.length > 256)
51247
+ refuse2();
51248
+ for (const [name, server] of servers) {
51249
+ if (!identifier2(name) || !object5(server) || !Object.keys(server).every((key) => ["command", "args", "cwd", "env_vars"].includes(key)) || !textField(server.command) || server.args !== undefined && (!Array.isArray(server.args) || server.args.length > 256 || !server.args.every((arg) => textField(arg, true))) || server.cwd !== undefined && !textField(server.cwd))
51250
+ refuse2();
51251
+ if (server.env_vars !== undefined) {
51252
+ if (!Array.isArray(server.env_vars) || server.env_vars.length > 256)
51253
+ refuse2();
51254
+ const names = server.env_vars.map((entry) => {
51255
+ if (envName(entry))
51256
+ return entry;
51257
+ if (!object5(entry) || !Object.keys(entry).every((key) => ["name", "source"].includes(key)) || !envName(entry.name) || entry.source !== undefined && entry.source !== "local")
51258
+ refuse2();
51259
+ return entry.name;
51260
+ });
51261
+ if (new Set(names).size !== names.length)
51262
+ refuse2();
51263
+ }
51264
+ }
51265
+ return sha2562;
51266
+ }
51229
51267
  function rootControls(root, read2) {
51230
51268
  const manifestPath = join18(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync12(manifestPath, { throwIfNoEntry: false });
51231
51269
  if (!lstatSync12(join18(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
@@ -51243,15 +51281,21 @@ function rootControls(root, read2) {
51243
51281
  const cacheVersion = basename5(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
51244
51282
  if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
51245
51283
  refuse2();
51246
- if (manifest.hooks !== undefined || manifest.mcpServers !== undefined || manifest.commands !== undefined || manifest.apps !== undefined && manifest.apps !== "./.app.json" || ["hooks", ".mcp.json"].some((path) => lstatSync12(join18(root, path), { throwIfNoEntry: false })))
51284
+ 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 }))
51285
+ refuse2();
51286
+ const mcp = join18(root, ".mcp.json"), mcpPresent = lstatSync12(mcp, { throwIfNoEntry: false });
51287
+ if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync5(mcp) !== mcp))
51288
+ refuse2();
51289
+ if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
51247
51290
  refuse2();
51291
+ const mcpSha256 = mcpPresent ? mcpControls(mcp, read2) : undefined;
51248
51292
  const app = join18(root, ".app.json"), present = lstatSync12(app, { throwIfNoEntry: false });
51249
51293
  if (present && (!present.isFile() || present.isSymbolicLink() || present.size > 1024 * 1024))
51250
51294
  refuse2();
51251
51295
  if (manifest.apps !== undefined && !present)
51252
51296
  refuse2();
51253
51297
  const appSha256 = present ? appControls(app, read2) : undefined;
51254
- return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {} };
51298
+ return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
51255
51299
  }
51256
51300
  function identity4(document3, cache3, read2) {
51257
51301
  if (!document3.startsWith(cache3 + sep4) || !document3.endsWith(sep4 + "SKILL.md"))
@@ -51314,7 +51358,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache3, controls, read2) {
51314
51358
  if (!stat.isDirectory() || ![".codex-plugin/plugin.json", ".app.json", "hooks", ".mcp.json"].some((path) => lstatSync12(join18(root, path), { throwIfNoEntry: false })))
51315
51359
  continue;
51316
51360
  const parsed = rootControls(root, read2);
51317
- if (!controls.some((control) => control.pluginParent === parent && control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && control.appSha256 === parsed.appSha256))
51361
+ if (!controls.some((control) => control.pluginParent === parent && control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && control.appSha256 === parsed.appSha256 && control.mcpSha256 === parsed.mcpSha256))
51318
51362
  return false;
51319
51363
  }
51320
51364
  }
@@ -51374,7 +51418,7 @@ function reviewCodexPluginSkillControls(catalog, documents, cache3, cwd2, read2,
51374
51418
  }
51375
51419
  result2.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
51376
51420
  }
51377
- if (!result2.length || new Set(result2.map((item) => item.name)).size !== result2.length)
51421
+ if (!result2.length && documents.some((document3) => document3.startsWith(cache3 + sep4)) || new Set(result2.map((item) => item.name)).size !== result2.length)
51378
51422
  refuse2();
51379
51423
  return result2;
51380
51424
  }
@@ -51387,7 +51431,7 @@ function isReviewedCodexPluginSkillDisabled(document3, cache3, controls, rules,
51387
51431
  } catch {
51388
51432
  return false;
51389
51433
  }
51390
- const reviewed = controls.find((item) => item.name === parsed.name && item.namespace === parsed.namespace && item.pluginParent === parsed.pluginParent && item.manifestSha256 === parsed.manifestSha256 && item.appSha256 === parsed.appSha256);
51434
+ const reviewed = controls.find((item) => item.name === parsed.name && item.namespace === parsed.namespace && item.pluginParent === parsed.pluginParent && item.manifestSha256 === parsed.manifestSha256 && item.appSha256 === parsed.appSha256 && item.mcpSha256 === parsed.mcpSha256);
51391
51435
  if (!reviewed || reviewed.remotePluginId !== undefined && !remoteInstallationMatches(reviewed.pluginParent, reviewed.remotePluginId, read2))
51392
51436
  return false;
51393
51437
  return isCodexNativeSkillDisabled({ name: parsed.name, path: document3, pluginId: reviewed.pluginId, enabled: true }, rules);
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.22",
20022
+ version: "0.10.23",
20023
20023
  description: "Skills library for AI coding agents",
20024
20024
  type: "module",
20025
20025
  bin: {
@@ -38253,7 +38253,7 @@ function assertAgentPolicyCollections(policy) {
38253
38253
  }
38254
38254
  if (bridge.codexPluginSkills !== undefined)
38255
38255
  for (const control of array3(bridge.codexPluginSkills, 4096)) {
38256
- requireBound(object4(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "remotePluginId"].includes(key)));
38256
+ requireBound(object4(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "mcpSha256", "remotePluginId"].includes(key)));
38257
38257
  text(control.name, 129);
38258
38258
  text(control.pluginId, 1024);
38259
38259
  text(control.namespace, 64);
@@ -38261,6 +38261,8 @@ function assertAgentPolicyCollections(policy) {
38261
38261
  requireBound(isAbsolute3(control.pluginParent) && resolve2(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
38262
38262
  if (control.remotePluginId !== undefined)
38263
38263
  requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
38264
+ if (control.mcpSha256 !== undefined)
38265
+ requireBound(typeof control.mcpSha256 === "string" && /^[a-f0-9]{64}$/.test(control.mcpSha256));
38264
38266
  if (control.appSha256 !== undefined)
38265
38267
  requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
38266
38268
  }
package/bin/migrate.js CHANGED
@@ -7,7 +7,7 @@ import { join as join5 } from "path";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@hasna/skills",
10
- version: "0.10.22",
10
+ version: "0.10.23",
11
11
  description: "Skills library for AI coding agents",
12
12
  type: "module",
13
13
  bin: {
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.22",
24125
+ version: "0.10.23",
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.22",
24128
+ version: "0.10.23",
24129
24129
  description: "Skills library for AI coding agents",
24130
24130
  type: "module",
24131
24131
  bin: {
package/dist/index.js CHANGED
@@ -9038,7 +9038,7 @@ function assertAgentPolicyCollections(policy) {
9038
9038
  }
9039
9039
  if (bridge.codexPluginSkills !== undefined)
9040
9040
  for (const control of array(bridge.codexPluginSkills, 4096)) {
9041
- requireBound(object(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "remotePluginId"].includes(key)));
9041
+ requireBound(object(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "mcpSha256", "remotePluginId"].includes(key)));
9042
9042
  text(control.name, 129);
9043
9043
  text(control.pluginId, 1024);
9044
9044
  text(control.namespace, 64);
@@ -9046,6 +9046,8 @@ function assertAgentPolicyCollections(policy) {
9046
9046
  requireBound(isAbsolute2(control.pluginParent) && resolve2(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
9047
9047
  if (control.remotePluginId !== undefined)
9048
9048
  requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
9049
+ if (control.mcpSha256 !== undefined)
9050
+ requireBound(typeof control.mcpSha256 === "string" && /^[a-f0-9]{64}$/.test(control.mcpSha256));
9049
9051
  if (control.appSha256 !== undefined)
9050
9052
  requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
9051
9053
  }
@@ -10879,7 +10881,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
10879
10881
  // package.json
10880
10882
  var package_default = {
10881
10883
  name: "@hasna/skills",
10882
- version: "0.10.22",
10884
+ version: "0.10.23",
10883
10885
  description: "Skills library for AI coding agents",
10884
10886
  type: "module",
10885
10887
  bin: {
@@ -13367,6 +13369,42 @@ function appControls(path, read2) {
13367
13369
  }
13368
13370
  return sha2562;
13369
13371
  }
13372
+ function mcpControls(path, read2) {
13373
+ const text2 = read2(path);
13374
+ let value, sha2562;
13375
+ try {
13376
+ sha2562 = hashNativeJsonControls(text2);
13377
+ value = JSON.parse(text2);
13378
+ } catch {
13379
+ refuse2();
13380
+ }
13381
+ const object5 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
13382
+ const textField = (v, empty = false) => typeof v === "string" && (empty || Boolean(v.trim())) && Buffer.byteLength(v) <= 16384 && !/[\x00-\x1f\x7f]/.test(v);
13383
+ const envName = (v) => typeof v === "string" && /^[A-Za-z_][A-Za-z0-9_]{0,255}$/.test(v);
13384
+ if (!object5(value) || Object.keys(value).length !== 1 || !object5(value.mcpServers))
13385
+ refuse2();
13386
+ const servers = Object.entries(value.mcpServers);
13387
+ if (!servers.length || servers.length > 256)
13388
+ refuse2();
13389
+ for (const [name, server] of servers) {
13390
+ if (!identifier2(name) || !object5(server) || !Object.keys(server).every((key) => ["command", "args", "cwd", "env_vars"].includes(key)) || !textField(server.command) || server.args !== undefined && (!Array.isArray(server.args) || server.args.length > 256 || !server.args.every((arg) => textField(arg, true))) || server.cwd !== undefined && !textField(server.cwd))
13391
+ refuse2();
13392
+ if (server.env_vars !== undefined) {
13393
+ if (!Array.isArray(server.env_vars) || server.env_vars.length > 256)
13394
+ refuse2();
13395
+ const names = server.env_vars.map((entry) => {
13396
+ if (envName(entry))
13397
+ return entry;
13398
+ if (!object5(entry) || !Object.keys(entry).every((key) => ["name", "source"].includes(key)) || !envName(entry.name) || entry.source !== undefined && entry.source !== "local")
13399
+ refuse2();
13400
+ return entry.name;
13401
+ });
13402
+ if (new Set(names).size !== names.length)
13403
+ refuse2();
13404
+ }
13405
+ }
13406
+ return sha2562;
13407
+ }
13370
13408
  function rootControls(root, read2) {
13371
13409
  const manifestPath = join14(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync12(manifestPath, { throwIfNoEntry: false });
13372
13410
  if (!lstatSync12(join14(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
@@ -13384,15 +13422,21 @@ function rootControls(root, read2) {
13384
13422
  const cacheVersion = basename5(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
13385
13423
  if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
13386
13424
  refuse2();
13387
- if (manifest.hooks !== undefined || manifest.mcpServers !== undefined || manifest.commands !== undefined || manifest.apps !== undefined && manifest.apps !== "./.app.json" || ["hooks", ".mcp.json"].some((path) => lstatSync12(join14(root, path), { throwIfNoEntry: false })))
13425
+ 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 }))
13426
+ refuse2();
13427
+ const mcp = join14(root, ".mcp.json"), mcpPresent = lstatSync12(mcp, { throwIfNoEntry: false });
13428
+ if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync5(mcp) !== mcp))
13429
+ refuse2();
13430
+ if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
13388
13431
  refuse2();
13432
+ const mcpSha256 = mcpPresent ? mcpControls(mcp, read2) : undefined;
13389
13433
  const app = join14(root, ".app.json"), present = lstatSync12(app, { throwIfNoEntry: false });
13390
13434
  if (present && (!present.isFile() || present.isSymbolicLink() || present.size > 1024 * 1024))
13391
13435
  refuse2();
13392
13436
  if (manifest.apps !== undefined && !present)
13393
13437
  refuse2();
13394
13438
  const appSha256 = present ? appControls(app, read2) : undefined;
13395
- return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {} };
13439
+ return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
13396
13440
  }
13397
13441
  function identity3(document2, cache, read2) {
13398
13442
  if (!document2.startsWith(cache + sep4) || !document2.endsWith(sep4 + "SKILL.md"))
@@ -13455,7 +13499,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
13455
13499
  if (!stat.isDirectory() || ![".codex-plugin/plugin.json", ".app.json", "hooks", ".mcp.json"].some((path) => lstatSync12(join14(root, path), { throwIfNoEntry: false })))
13456
13500
  continue;
13457
13501
  const parsed = rootControls(root, read2);
13458
- if (!controls.some((control) => control.pluginParent === parent && control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && control.appSha256 === parsed.appSha256))
13502
+ if (!controls.some((control) => control.pluginParent === parent && control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && control.appSha256 === parsed.appSha256 && control.mcpSha256 === parsed.mcpSha256))
13459
13503
  return false;
13460
13504
  }
13461
13505
  }
@@ -13515,7 +13559,7 @@ function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read2, r
13515
13559
  }
13516
13560
  result.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
13517
13561
  }
13518
- if (!result.length || new Set(result.map((item) => item.name)).size !== result.length)
13562
+ if (!result.length && documents.some((document2) => document2.startsWith(cache + sep4)) || new Set(result.map((item) => item.name)).size !== result.length)
13519
13563
  refuse2();
13520
13564
  return result;
13521
13565
  }
@@ -13528,7 +13572,7 @@ function isReviewedCodexPluginSkillDisabled(document2, cache, controls, rules, r
13528
13572
  } catch {
13529
13573
  return false;
13530
13574
  }
13531
- const reviewed = controls.find((item) => item.name === parsed.name && item.namespace === parsed.namespace && item.pluginParent === parsed.pluginParent && item.manifestSha256 === parsed.manifestSha256 && item.appSha256 === parsed.appSha256);
13575
+ const reviewed = controls.find((item) => item.name === parsed.name && item.namespace === parsed.namespace && item.pluginParent === parsed.pluginParent && item.manifestSha256 === parsed.manifestSha256 && item.appSha256 === parsed.appSha256 && item.mcpSha256 === parsed.mcpSha256);
13532
13576
  if (!reviewed || reviewed.remotePluginId !== undefined && !remoteInstallationMatches(reviewed.pluginParent, reviewed.remotePluginId, read2))
13533
13577
  return false;
13534
13578
  return isCodexNativeSkillDisabled({ name: parsed.name, path: document2, pluginId: reviewed.pluginId, enabled: true }, rules);
@@ -6,6 +6,7 @@ export interface CodexPluginSkillControl {
6
6
  pluginParent: string;
7
7
  manifestSha256: string;
8
8
  appSha256?: string;
9
+ mcpSha256?: string;
9
10
  remotePluginId?: string;
10
11
  }
11
12
  type Read = (path: string) => string;
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.22",
32376
+ version: "0.10.23",
32377
32377
  description: "Skills library for AI coding agents",
32378
32378
  type: "module",
32379
32379
  bin: {
@@ -38347,6 +38347,42 @@ function appControls(path, read) {
38347
38347
  }
38348
38348
  return sha2562;
38349
38349
  }
38350
+ function mcpControls(path, read) {
38351
+ const text = read(path);
38352
+ let value, sha2562;
38353
+ try {
38354
+ sha2562 = hashNativeJsonControls(text);
38355
+ value = JSON.parse(text);
38356
+ } catch {
38357
+ refuse3();
38358
+ }
38359
+ const object3 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
38360
+ const textField = (v, empty = false) => typeof v === "string" && (empty || Boolean(v.trim())) && Buffer.byteLength(v) <= 16384 && !/[\x00-\x1f\x7f]/.test(v);
38361
+ const envName = (v) => typeof v === "string" && /^[A-Za-z_][A-Za-z0-9_]{0,255}$/.test(v);
38362
+ if (!object3(value) || Object.keys(value).length !== 1 || !object3(value.mcpServers))
38363
+ refuse3();
38364
+ const servers = Object.entries(value.mcpServers);
38365
+ if (!servers.length || servers.length > 256)
38366
+ refuse3();
38367
+ for (const [name, server] of servers) {
38368
+ if (!identifier3(name) || !object3(server) || !Object.keys(server).every((key) => ["command", "args", "cwd", "env_vars"].includes(key)) || !textField(server.command) || server.args !== undefined && (!Array.isArray(server.args) || server.args.length > 256 || !server.args.every((arg) => textField(arg, true))) || server.cwd !== undefined && !textField(server.cwd))
38369
+ refuse3();
38370
+ if (server.env_vars !== undefined) {
38371
+ if (!Array.isArray(server.env_vars) || server.env_vars.length > 256)
38372
+ refuse3();
38373
+ const names = server.env_vars.map((entry) => {
38374
+ if (envName(entry))
38375
+ return entry;
38376
+ if (!object3(entry) || !Object.keys(entry).every((key) => ["name", "source"].includes(key)) || !envName(entry.name) || entry.source !== undefined && entry.source !== "local")
38377
+ refuse3();
38378
+ return entry.name;
38379
+ });
38380
+ if (new Set(names).size !== names.length)
38381
+ refuse3();
38382
+ }
38383
+ }
38384
+ return sha2562;
38385
+ }
38350
38386
  function rootControls(root, read) {
38351
38387
  const manifestPath = join10(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync6(manifestPath, { throwIfNoEntry: false });
38352
38388
  if (!lstatSync6(join10(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
@@ -38364,15 +38400,21 @@ function rootControls(root, read) {
38364
38400
  const cacheVersion = basename3(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
38365
38401
  if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
38366
38402
  refuse3();
38367
- if (manifest.hooks !== undefined || manifest.mcpServers !== undefined || manifest.commands !== undefined || manifest.apps !== undefined && manifest.apps !== "./.app.json" || ["hooks", ".mcp.json"].some((path) => lstatSync6(join10(root, path), { throwIfNoEntry: false })))
38403
+ 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 }))
38404
+ refuse3();
38405
+ const mcp = join10(root, ".mcp.json"), mcpPresent = lstatSync6(mcp, { throwIfNoEntry: false });
38406
+ if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync5(mcp) !== mcp))
38407
+ refuse3();
38408
+ if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
38368
38409
  refuse3();
38410
+ const mcpSha256 = mcpPresent ? mcpControls(mcp, read) : undefined;
38369
38411
  const app = join10(root, ".app.json"), present = lstatSync6(app, { throwIfNoEntry: false });
38370
38412
  if (present && (!present.isFile() || present.isSymbolicLink() || present.size > 1024 * 1024))
38371
38413
  refuse3();
38372
38414
  if (manifest.apps !== undefined && !present)
38373
38415
  refuse3();
38374
38416
  const appSha256 = present ? appControls(app, read) : undefined;
38375
- return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {} };
38417
+ return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
38376
38418
  }
38377
38419
  function identity(document, cache, read) {
38378
38420
  if (!document.startsWith(cache + sep2) || !document.endsWith(sep2 + "SKILL.md"))
@@ -38435,7 +38477,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read) {
38435
38477
  if (!stat.isDirectory() || ![".codex-plugin/plugin.json", ".app.json", "hooks", ".mcp.json"].some((path) => lstatSync6(join10(root, path), { throwIfNoEntry: false })))
38436
38478
  continue;
38437
38479
  const parsed = rootControls(root, read);
38438
- if (!controls.some((control) => control.pluginParent === parent && control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && control.appSha256 === parsed.appSha256))
38480
+ if (!controls.some((control) => control.pluginParent === parent && control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && control.appSha256 === parsed.appSha256 && control.mcpSha256 === parsed.mcpSha256))
38439
38481
  return false;
38440
38482
  }
38441
38483
  }
@@ -38495,7 +38537,7 @@ function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read, ru
38495
38537
  }
38496
38538
  result.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
38497
38539
  }
38498
- if (!result.length || new Set(result.map((item) => item.name)).size !== result.length)
38540
+ if (!result.length && documents.some((document) => document.startsWith(cache + sep2)) || new Set(result.map((item) => item.name)).size !== result.length)
38499
38541
  refuse3();
38500
38542
  return result;
38501
38543
  }
@@ -38508,7 +38550,7 @@ function isReviewedCodexPluginSkillDisabled(document, cache, controls, rules, re
38508
38550
  } catch {
38509
38551
  return false;
38510
38552
  }
38511
- const reviewed = controls.find((item) => item.name === parsed.name && item.namespace === parsed.namespace && item.pluginParent === parsed.pluginParent && item.manifestSha256 === parsed.manifestSha256 && item.appSha256 === parsed.appSha256);
38553
+ const reviewed = controls.find((item) => item.name === parsed.name && item.namespace === parsed.namespace && item.pluginParent === parsed.pluginParent && item.manifestSha256 === parsed.manifestSha256 && item.appSha256 === parsed.appSha256 && item.mcpSha256 === parsed.mcpSha256);
38512
38554
  if (!reviewed || reviewed.remotePluginId !== undefined && !remoteInstallationMatches(reviewed.pluginParent, reviewed.remotePluginId, read))
38513
38555
  return false;
38514
38556
  return isCodexNativeSkillDisabled({ name: parsed.name, path: document, pluginId: reviewed.pluginId, enabled: true }, rules);
@@ -38860,7 +38902,7 @@ function assertAgentPolicyCollections(policy) {
38860
38902
  }
38861
38903
  if (bridge.codexPluginSkills !== undefined)
38862
38904
  for (const control of array(bridge.codexPluginSkills, 4096)) {
38863
- requireBound(object3(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "remotePluginId"].includes(key)));
38905
+ requireBound(object3(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "mcpSha256", "remotePluginId"].includes(key)));
38864
38906
  text(control.name, 129);
38865
38907
  text(control.pluginId, 1024);
38866
38908
  text(control.namespace, 64);
@@ -38868,6 +38910,8 @@ function assertAgentPolicyCollections(policy) {
38868
38910
  requireBound(isAbsolute6(control.pluginParent) && resolve5(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
38869
38911
  if (control.remotePluginId !== undefined)
38870
38912
  requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
38913
+ if (control.mcpSha256 !== undefined)
38914
+ requireBound(typeof control.mcpSha256 === "string" && /^[a-f0-9]{64}$/.test(control.mcpSha256));
38871
38915
  if (control.appSha256 !== undefined)
38872
38916
  requireBound(typeof control.appSha256 === "string" && /^[a-f0-9]{64}$/.test(control.appSha256));
38873
38917
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/skills",
3
- "version": "0.10.22",
3
+ "version": "0.10.23",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {