@hasna/skills 0.10.65 → 0.10.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7262,7 +7262,7 @@ import {
7262
7262
  statSync as statSync12,
7263
7263
  writeFileSync as writeFileSync9
7264
7264
  } from "fs";
7265
- import { basename as basename13, dirname as dirname25, isAbsolute as isAbsolute25, join as join37, normalize as normalize4, relative as relative10, resolve as resolve25, sep as sep13 } from "path";
7265
+ import { basename as basename15, dirname as dirname25, isAbsolute as isAbsolute25, join as join37, normalize as normalize4, relative as relative10, resolve as resolve25, sep as sep13 } from "path";
7266
7266
 
7267
7267
  // src/lib/registry-data/index.ts
7268
7268
  var SKILLS = [];
@@ -8922,7 +8922,7 @@ var NATIVE_SKILL_ROOTS = [
8922
8922
  // src/lib/authoring-path.ts
8923
8923
  import { lstatSync as lstatSync29, realpathSync as realpathSync14 } from "fs";
8924
8924
  import { homedir as homedir6 } from "os";
8925
- import { basename as basename12, dirname as dirname24, join as join36, resolve as resolve24, sep as sep12 } from "path";
8925
+ import { basename as basename14, dirname as dirname24, join as join36, resolve as resolve24, sep as sep12 } from "path";
8926
8926
 
8927
8927
  // src/lib/agent-adapters.ts
8928
8928
  var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes", "sumi"];
@@ -9744,8 +9744,8 @@ function assertAgentPolicyCollections(policy) {
9744
9744
  }
9745
9745
  }
9746
9746
  if (value.codexSemanticCache !== undefined) {
9747
- const proof = record(value.codexSemanticCache, 8), witness = record(proof.witness, 6);
9748
- requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).length === 8 && proof.schema === "hasna.codex-semantic-cache-discovery.v1" && typeof proof.previousPolicySha256 === "string" && /^[a-f0-9]{64}$/.test(proof.previousPolicySha256) && Object.keys(witness).length === 6 && witness.schema === "hasna.codex-semantic-cache-witness.v1" && typeof witness.sha256 === "string" && /^[a-f0-9]{64}$/.test(witness.sha256));
9747
+ const proof = record(value.codexSemanticCache, 9), witness = record(proof.witness, 7);
9748
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && [8, 9].includes(Object.keys(proof).length) && Object.keys(proof).every((key) => ["schema", "configPath", "hookRootAlias", "witness", "appOnlyParents", "previousPolicySha256", "supersededSources", "supersededDirectories", "reviewedHookParents"].includes(key)) && proof.schema === "hasna.codex-semantic-cache-discovery.v1" && typeof proof.previousPolicySha256 === "string" && /^[a-f0-9]{64}$/.test(proof.previousPolicySha256) && [6, 7].includes(Object.keys(witness).length) && Object.keys(witness).every((key) => ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256", "hookParents"].includes(key)) && witness.schema === "hasna.codex-semantic-cache-witness.v1" && typeof witness.sha256 === "string" && /^[a-f0-9]{64}$/.test(witness.sha256));
9749
9749
  text(proof.configPath);
9750
9750
  requireBound(isAbsolute4(proof.configPath) && resolve4(proof.configPath) === proof.configPath && basename4(proof.configPath) === "config.toml");
9751
9751
  text(witness.cacheRoot);
@@ -9764,6 +9764,20 @@ function assertAgentPolicyCollections(policy) {
9764
9764
  text(parent[key]);
9765
9765
  requireBound(isAbsolute4(parent.pluginParent) && resolve4(parent.pluginParent) === parent.pluginParent);
9766
9766
  }
9767
+ if (proof.reviewedHookParents !== undefined)
9768
+ for (const parent of array(proof.reviewedHookParents, 4096)) {
9769
+ requireBound(object(parent) && parent.role === "reviewed-hook" && (parent.installation === "remote-receipt" && Object.keys(parent).length === 6 || parent.installation === "local-marketplace" && Object.keys(parent).length === 7));
9770
+ requireBound(parent.installation === "remote-receipt" ? Object.keys(parent).sort().join(",") === "installation,namespace,pluginId,pluginParent,remotePluginId,role" : Object.keys(parent).sort().join(",") === "installation,marketplaceSha256,marketplaceSourcePath,namespace,pluginId,pluginParent,role");
9771
+ for (const key of ["pluginId", "namespace", "pluginParent", "installation"])
9772
+ text(parent[key]);
9773
+ if (parent.installation === "remote-receipt")
9774
+ text(parent.remotePluginId);
9775
+ else {
9776
+ text(parent.marketplaceSourcePath);
9777
+ requireBound(typeof parent.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256) && isAbsolute4(parent.marketplaceSourcePath) && resolve4(parent.marketplaceSourcePath) === parent.marketplaceSourcePath && basename4(parent.marketplaceSourcePath) === "marketplace.json");
9778
+ }
9779
+ requireBound(isAbsolute4(parent.pluginParent) && resolve4(parent.pluginParent) === parent.pluginParent);
9780
+ }
9767
9781
  for (const row of [...array(witness.controls, 4096), ...array(witness.appOnlyParents, 4096)]) {
9768
9782
  requireBound(object(row));
9769
9783
  for (const key of ["pluginId", "namespace", "pluginParent"])
@@ -9772,6 +9786,26 @@ function assertAgentPolicyCollections(policy) {
9772
9786
  if (row[key] !== undefined)
9773
9787
  requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
9774
9788
  }
9789
+ if (witness.hookParents !== undefined)
9790
+ for (const row of array(witness.hookParents, 4096)) {
9791
+ requireBound(object(row) && row.role === "reviewed-hook" && (row.installation === "remote-receipt" && Object.keys(row).length === 9 || row.installation === "local-marketplace" && Object.keys(row).length === 9));
9792
+ requireBound(row.installation === "remote-receipt" ? Object.keys(row).sort().join(",") === "hooksSha256,installation,manifestSha256,namespace,pluginId,pluginParent,receiptSha256,remotePluginId,role" : Object.keys(row).sort().join(",") === "hooksSha256,installation,manifestSha256,marketplaceSha256,marketplaceSourcePath,namespace,pluginId,pluginParent,role");
9793
+ for (const key of ["pluginId", "namespace", "pluginParent", "installation"])
9794
+ text(row[key]);
9795
+ if (row.installation === "remote-receipt")
9796
+ text(row.remotePluginId);
9797
+ else {
9798
+ text(row.marketplaceSourcePath);
9799
+ requireBound(typeof row.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(row.marketplaceSha256) && isAbsolute4(row.marketplaceSourcePath) && resolve4(row.marketplaceSourcePath) === row.marketplaceSourcePath && basename4(row.marketplaceSourcePath) === "marketplace.json");
9800
+ }
9801
+ requireBound(isAbsolute4(row.pluginParent) && resolve4(row.pluginParent) === row.pluginParent);
9802
+ for (const key of ["manifestSha256", "hooksSha256"])
9803
+ requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
9804
+ if (row.installation === "remote-receipt")
9805
+ requireBound(typeof row.receiptSha256 === "string" && /^[a-f0-9]{64}$/.test(row.receiptSha256));
9806
+ else
9807
+ requireBound(row.receiptSha256 === undefined);
9808
+ }
9775
9809
  for (const skill of array(witness.skills, 4096)) {
9776
9810
  requireBound(object(skill));
9777
9811
  for (const key of ["name", "pluginId", "namespace", "pluginParent"])
@@ -9851,7 +9885,7 @@ function assertAgentPolicyCollections(policy) {
9851
9885
  // src/lib/agent-discovery.ts
9852
9886
  import { createHash as createHash28 } from "crypto";
9853
9887
  import { lstatSync as lstatSync28, readFileSync as readFileSync21, readdirSync as readdirSync11, realpathSync as realpathSync13 } from "fs";
9854
- import { basename as basename11, dirname as dirname23, isAbsolute as isAbsolute24, join as join35, relative as relative9, resolve as resolve23, sep as sep11 } from "path";
9888
+ import { basename as basename13, dirname as dirname23, isAbsolute as isAbsolute24, join as join35, relative as relative9, resolve as resolve23, sep as sep11 } from "path";
9855
9889
  import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
9856
9890
 
9857
9891
  // node_modules/yaml/dist/index.js
@@ -10542,7 +10576,7 @@ function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
10542
10576
 
10543
10577
  // src/lib/plugin-discovery.ts
10544
10578
  import { closeSync as closeSync16, constants as constants17, fstatSync as fstatSync16, lstatSync as lstatSync26, openSync as openSync16, opendirSync as opendirSync5, readSync as readSync12 } from "fs";
10545
- import { basename as basename9, dirname as dirname21, isAbsolute as isAbsolute22, join as join34, resolve as resolve21 } from "path";
10579
+ import { basename as basename11, dirname as dirname21, isAbsolute as isAbsolute22, join as join34, resolve as resolve21 } from "path";
10546
10580
 
10547
10581
  // src/lib/plugin-admission.ts
10548
10582
  import { homedir as homedir5 } from "os";
@@ -11888,7 +11922,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11888
11922
  });
11889
11923
  }
11890
11924
  // package.json
11891
- var version = "0.10.65";
11925
+ var version = "0.10.67";
11892
11926
  var package_default = {
11893
11927
  name: "@hasna/skills",
11894
11928
  version,
@@ -14246,7 +14280,7 @@ import {
14246
14280
  writeFileSync as writeFileSync7
14247
14281
  } from "fs";
14248
14282
  import { homedir as homedir3 } from "os";
14249
- import { basename as basename8, dirname as dirname19, join as join28 } from "path";
14283
+ import { basename as basename10, dirname as dirname19, join as join28 } from "path";
14250
14284
 
14251
14285
  // src/lib/codex-plugin-skill-controls.ts
14252
14286
  import { createHash as createHash14 } from "crypto";
@@ -16178,7 +16212,7 @@ function upgradeCodexSettingsWitnessV5(previous, reviewedSettingsPath) {
16178
16212
  // src/lib/codex-semantic-cache-witness.ts
16179
16213
  import { createHash as createHash18 } from "crypto";
16180
16214
  import { constants as constants9, closeSync as closeSync9, fstatSync as fstatSync9, lstatSync as lstatSync16, openSync as openSync9, readSync as readSync6, readdirSync as readdirSync7, realpathSync as realpathSync9 } from "fs";
16181
- import { dirname as dirname15, isAbsolute as isAbsolute14, join as join20, relative as relative7, resolve as resolve14, sep as sep8 } from "path";
16215
+ import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute14, join as join20, relative as relative7, resolve as resolve14, sep as sep8 } from "path";
16182
16216
  var CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA = "hasna.codex-semantic-cache-witness.v1";
16183
16217
  var identifier3 = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(value);
16184
16218
  var digest = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
@@ -16187,6 +16221,26 @@ var refuse4 = () => {
16187
16221
  };
16188
16222
  var MAX_DOCUMENT_BYTES = 1024 * 1024;
16189
16223
  var MAX_RECEIPT_BYTES = 16 * 1024;
16224
+ function codexLocalMarketplaceMapsPlugin(text2, marketplace, plugin) {
16225
+ try {
16226
+ hashNativeJsonControls(text2);
16227
+ const value = JSON.parse(text2);
16228
+ const marketplaceInterface = plainObject(value) ? value.interface : undefined;
16229
+ const validInterface = typeof marketplaceInterface === "string" || Number.isSafeInteger(marketplaceInterface) || plainObject(marketplaceInterface) && exactKeys(marketplaceInterface, [], ["displayName"]) && typeof marketplaceInterface.displayName === "string" && marketplaceInterface.displayName.trim().length > 0;
16230
+ if (!plainObject(value) || !exactKeys(value, ["interface", "name", "plugins"]) || value.name !== marketplace || !validInterface || !Array.isArray(value.plugins) || value.plugins.length < 1 || value.plugins.length > 4096)
16231
+ return false;
16232
+ const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
16233
+ if (rows.length !== 1)
16234
+ return false;
16235
+ const row = rows[0];
16236
+ if (!exactKeys(row, ["name", "source"]) || !plainObject(row.source) || !exactKeys(row.source, ["source", "path"]) || row.source.source !== "local" || typeof row.source.path !== "string" || !row.source.path.startsWith("./") || row.source.path.includes("\\"))
16237
+ return false;
16238
+ const parts = row.source.path.split("/");
16239
+ return parts[0] === "." && parts.length > 1 && parts.slice(1).every((part) => /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(part)) && parts.at(-1) === plugin;
16240
+ } catch {
16241
+ return false;
16242
+ }
16243
+ }
16190
16244
  function defaultRead(path) {
16191
16245
  const before = lstatSync16(path, { throwIfNoEntry: false, bigint: true });
16192
16246
  if (before === undefined)
@@ -16248,7 +16302,7 @@ function validPluginParent(cacheRoot, pluginParent, namespace, pluginId) {
16248
16302
  return parts.length === 2 && parts.every(identifier3) && parts[1] === namespace && pluginId === `${namespace}@${parts[0]}`;
16249
16303
  }
16250
16304
  function assertWitnessShape(value) {
16251
- if (!plainObject(value) || !exactKeys(value, ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256"]) || value.schema !== CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA || typeof value.cacheRoot !== "string" || !isAbsolute14(value.cacheRoot) || resolve14(value.cacheRoot) !== value.cacheRoot || !digest(value.sha256) || !Array.isArray(value.controls) || value.controls.length === 0 || value.controls.length > 4096 || !Array.isArray(value.appOnlyParents) || value.appOnlyParents.length > 4096 || !Array.isArray(value.skills) || value.skills.length === 0 || value.skills.length > 4096)
16305
+ if (!plainObject(value) || !exactKeys(value, ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256"], ["hookParents"]) || value.schema !== CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA || typeof value.cacheRoot !== "string" || !isAbsolute14(value.cacheRoot) || resolve14(value.cacheRoot) !== value.cacheRoot || !digest(value.sha256) || !Array.isArray(value.controls) || value.controls.length === 0 || value.controls.length > 4096 || !Array.isArray(value.appOnlyParents) || value.appOnlyParents.length > 4096 || !Array.isArray(value.skills) || value.skills.length === 0 || value.skills.length > 4096)
16252
16306
  refuse4();
16253
16307
  const witness = value;
16254
16308
  const controlParents = new Set;
@@ -16269,6 +16323,20 @@ function assertWitnessShape(value) {
16269
16323
  refuse4();
16270
16324
  appOnlyParentPaths.add(row.pluginParent);
16271
16325
  }
16326
+ const hookParentPaths = new Set;
16327
+ const hookParents = witness.hookParents === undefined ? [] : witness.hookParents;
16328
+ if (!Array.isArray(hookParents) || hookParents.length > 4096)
16329
+ refuse4();
16330
+ for (const candidate of hookParents) {
16331
+ if (!plainObject(candidate))
16332
+ refuse4();
16333
+ const row = candidate;
16334
+ const remote = row.installation === "remote-receipt";
16335
+ const local = row.installation === "local-marketplace";
16336
+ if (!exactKeys(row, ["role", "pluginId", "namespace", "pluginParent", "installation", "manifestSha256", "hooksSha256"], remote ? ["remotePluginId", "receiptSha256"] : local ? ["marketplaceSourcePath", "marketplaceSha256"] : []) || row.role !== "reviewed-hook" || !identifier3(row.namespace) || typeof row.pluginId !== "string" || !/^[A-Za-z0-9_-]{1,64}@[A-Za-z0-9_-]{1,64}$/.test(row.pluginId) || typeof row.pluginParent !== "string" || !validPluginParent(String(witness.cacheRoot), row.pluginParent, row.namespace, row.pluginId) || remote && (typeof row.remotePluginId !== "string" || !/^[A-Za-z0-9_~-]{1,1024}$/.test(row.remotePluginId) || !digest(row.receiptSha256)) || local && (typeof row.marketplaceSourcePath !== "string" || !isAbsolute14(row.marketplaceSourcePath) || resolve14(row.marketplaceSourcePath) !== row.marketplaceSourcePath || basename8(row.marketplaceSourcePath) !== "marketplace.json" || !digest(row.marketplaceSha256)) || !remote && !local || !digest(row.manifestSha256) || !digest(row.hooksSha256) || hookParentPaths.has(row.pluginParent) || controlParents.has(row.pluginParent) || appOnlyParentPaths.has(row.pluginParent))
16337
+ refuse4();
16338
+ hookParentPaths.add(row.pluginParent);
16339
+ }
16272
16340
  const skillNames = new Set;
16273
16341
  const qualifiedNames = new Set;
16274
16342
  for (const candidate of witness.skills) {
@@ -16288,6 +16356,7 @@ function assertWitnessShape(value) {
16288
16356
  cacheRoot: witness.cacheRoot,
16289
16357
  controls: witness.controls,
16290
16358
  appOnlyParents: witness.appOnlyParents,
16359
+ ...witness.hookParents === undefined ? {} : { hookParents: witness.hookParents },
16291
16360
  skills: witness.skills
16292
16361
  };
16293
16362
  if (hash2(projection) !== witness.sha256)
@@ -16478,10 +16547,147 @@ function appOnlyParentProjection(cacheRoot, parents2, read2) {
16478
16547
  }
16479
16548
  return rows.sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.pluginParent.localeCompare(b.pluginParent));
16480
16549
  }
16481
- function assertParents(cacheRoot, controls, appOnlyParents, files, members) {
16550
+ var codexHookEvents = new Set([
16551
+ "SessionStart",
16552
+ "UserPromptSubmit",
16553
+ "PreToolUse",
16554
+ "PostToolUse",
16555
+ "PostToolUseFailure",
16556
+ "Stop",
16557
+ "SubagentStart",
16558
+ "SubagentStop",
16559
+ "PreCompact",
16560
+ "Notification",
16561
+ "PermissionRequest",
16562
+ "SessionEnd",
16563
+ "ConfigChange",
16564
+ "WorktreeCreate",
16565
+ "WorktreeRemove",
16566
+ "TeammateIdle",
16567
+ "TaskComplete"
16568
+ ]);
16569
+ function assertJsonValue(value, depth = 0, budget2 = { members: 0 }) {
16570
+ if (depth > 32 || ++budget2.members > 4096)
16571
+ refuse4();
16572
+ if (value === null || typeof value === "string" || typeof value === "boolean")
16573
+ return;
16574
+ if (typeof value === "number" && Number.isFinite(value))
16575
+ return;
16576
+ if (Array.isArray(value)) {
16577
+ for (const item of value)
16578
+ assertJsonValue(item, depth + 1, budget2);
16579
+ return;
16580
+ }
16581
+ if (!plainObject(value))
16582
+ refuse4();
16583
+ for (const [key, item] of Object.entries(value)) {
16584
+ if (!key || /[\x00-\x1f\x7f]/.test(key))
16585
+ refuse4();
16586
+ assertJsonValue(item, depth + 1, budget2);
16587
+ }
16588
+ }
16589
+ function reviewedHookControls(root, read2) {
16590
+ const manifestPath = join20(root, ".codex-plugin/plugin.json"), hooksPath = join20(root, "hooks/hooks.json");
16591
+ let manifest, manifestSha256, hooks, hooksSha256;
16592
+ try {
16593
+ const manifestText = read2(manifestPath);
16594
+ manifestSha256 = hashNativeJsonControls(manifestText, "version");
16595
+ manifest = JSON.parse(manifestText);
16596
+ const hooksText = read2(hooksPath);
16597
+ hashNativeJsonControls(hooksText);
16598
+ hooksSha256 = createHash18("sha256").update(hooksText).digest("hex");
16599
+ hooks = JSON.parse(hooksText);
16600
+ } catch {
16601
+ return refuse4();
16602
+ }
16603
+ if (!plainObject(manifest) || !exactKeys(manifest, ["name", "version", "hooks"], ["description"]) || !identifier3(manifest.name) || typeof manifest.version !== "string" || manifest.version !== basename8(root) || manifest.hooks !== "./hooks/hooks.json" || manifest.description !== undefined && (typeof manifest.description !== "string" || !manifest.description.trim() || manifest.description.length > 1024))
16604
+ refuse4();
16605
+ const manifestRecord = manifest;
16606
+ if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"], ["description"]) || !plainObject(hooks.hooks) || hooks.description !== undefined && typeof hooks.description !== "string")
16607
+ refuse4();
16608
+ const hookTable = hooks.hooks;
16609
+ const events = Object.entries(hookTable);
16610
+ if (!events.length || events.length > 32)
16611
+ refuse4();
16612
+ for (const [event, groups] of events) {
16613
+ if (!codexHookEvents.has(event) || !Array.isArray(groups) || !groups.length || groups.length > 32)
16614
+ refuse4();
16615
+ for (const group of groups) {
16616
+ if (!plainObject(group) || !exactKeys(group, ["hooks"]) || !Array.isArray(group.hooks) || !group.hooks.length || group.hooks.length > 32)
16617
+ refuse4();
16618
+ const handlers = group.hooks;
16619
+ for (const handler of handlers) {
16620
+ if (!plainObject(handler) || !exactKeys(handler, ["type", "server", "tool", "input"], ["timeout"]) || handler.type !== "mcp_tool" || !identifier3(handler.server) || !identifier3(handler.tool) || !plainObject(handler.input) || handler.timeout !== undefined && (!Number.isSafeInteger(handler.timeout) || Number(handler.timeout) < 1 || Number(handler.timeout) > 300))
16621
+ refuse4();
16622
+ assertJsonValue(handler.input);
16623
+ }
16624
+ }
16625
+ }
16626
+ return { namespace: manifestRecord.name, manifestSha256, hooksSha256 };
16627
+ }
16628
+ function reviewedHookParentProjection(cacheRoot, parents2, read2) {
16629
+ if (!Array.isArray(parents2) || parents2.length > 4096)
16630
+ refuse4();
16631
+ const seen = new Set, rows = [];
16632
+ for (const parent of parents2) {
16633
+ const remote = parent?.installation === "remote-receipt", local = parent?.installation === "local-marketplace";
16634
+ if (!parent || !exactKeys(parent, ["installation", "namespace", "pluginId", "pluginParent", "role"], remote ? ["remotePluginId"] : local ? ["marketplaceSourcePath", "marketplaceSha256"] : []) || parent.role !== "reviewed-hook" || !identifier3(parent.namespace) || !/^[A-Za-z0-9_-]{1,64}@[A-Za-z0-9_-]{1,64}$/.test(parent.pluginId) || typeof parent.pluginParent !== "string" || remote && (typeof parent.remotePluginId !== "string" || !/^[A-Za-z0-9_~-]{1,1024}$/.test(parent.remotePluginId)) || local && (typeof parent.marketplaceSourcePath !== "string" || !isAbsolute14(parent.marketplaceSourcePath) || resolve14(parent.marketplaceSourcePath) !== parent.marketplaceSourcePath || basename8(parent.marketplaceSourcePath) !== "marketplace.json" || !digest(parent.marketplaceSha256)) || !remote && !local)
16635
+ refuse4();
16636
+ const parts = relative7(cacheRoot, parent.pluginParent).split(sep8);
16637
+ if (!isAbsolute14(parent.pluginParent) || resolve14(parent.pluginParent) !== parent.pluginParent || parts.length !== 2 || parts.some((part) => !identifier3(part)) || parts[1] !== parent.namespace || parent.pluginId !== `${parent.namespace}@${parts[0]}` || seen.has(parent.pluginParent))
16638
+ refuse4();
16639
+ seen.add(parent.pluginParent);
16640
+ const parentStat = lstatSync16(parent.pluginParent, { throwIfNoEntry: false });
16641
+ if (!parentStat?.isDirectory() || parentStat.isSymbolicLink() || realpathSync9(parent.pluginParent) !== parent.pluginParent)
16642
+ refuse4();
16643
+ const receiptPath = join20(parent.pluginParent, ".codex-remote-plugin-install.json"), receiptStat = lstatSync16(receiptPath, { throwIfNoEntry: false });
16644
+ let receiptText;
16645
+ if (remote) {
16646
+ if (!receiptStat?.isFile() || receiptStat.isSymbolicLink() || receiptStat.size > MAX_RECEIPT_BYTES)
16647
+ refuse4();
16648
+ try {
16649
+ receiptText = read2(receiptPath);
16650
+ hashNativeJsonControls(receiptText);
16651
+ const receipt = JSON.parse(receiptText);
16652
+ if (!plainObject(receipt) || !exactKeys(receipt, ["schema_version", "remote_plugin_id"]) || receipt.schema_version !== 1 || receipt.remote_plugin_id !== parent.remotePluginId)
16653
+ refuse4();
16654
+ } catch {
16655
+ refuse4();
16656
+ }
16657
+ } else {
16658
+ const marketplaceSourcePath = parent.marketplaceSourcePath ?? refuse4();
16659
+ if (receiptStat !== undefined || !parent.marketplaceSha256)
16660
+ refuse4();
16661
+ let marketplaceText;
16662
+ try {
16663
+ marketplaceText = read2(marketplaceSourcePath);
16664
+ } catch {
16665
+ return refuse4();
16666
+ }
16667
+ if (createHash18("sha256").update(marketplaceText).digest("hex") !== parent.marketplaceSha256 || !codexLocalMarketplaceMapsPlugin(marketplaceText, parts[0], parent.namespace))
16668
+ refuse4();
16669
+ }
16670
+ const materializations = readdirSync7(parent.pluginParent).filter((name) => name !== ".codex-remote-plugin-install.json").map((version2) => {
16671
+ if (!/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2))
16672
+ refuse4();
16673
+ const root = join20(parent.pluginParent, version2), stat = lstatSync16(root, { throwIfNoEntry: false });
16674
+ if (!stat?.isDirectory() || stat.isSymbolicLink() || realpathSync9(root) !== root)
16675
+ refuse4();
16676
+ const controls = reviewedHookControls(root, read2);
16677
+ if (controls.namespace !== parent.namespace)
16678
+ refuse4();
16679
+ return { manifestSha256: controls.manifestSha256, hooksSha256: controls.hooksSha256 };
16680
+ });
16681
+ if (!materializations.length || materializations.some((item) => canonical7(item) !== canonical7(materializations[0])))
16682
+ refuse4();
16683
+ rows.push({ ...parent, ...receiptText === undefined ? {} : { receiptSha256: createHash18("sha256").update(receiptText).digest("hex") }, ...materializations[0] });
16684
+ }
16685
+ return rows.sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.pluginParent.localeCompare(b.pluginParent));
16686
+ }
16687
+ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files, members) {
16482
16688
  const expected = new Set;
16483
16689
  const expectedMarkets = new Set;
16484
- for (const control of [...controls, ...appOnlyParents]) {
16690
+ for (const control of [...controls, ...appOnlyParents, ...hookParents]) {
16485
16691
  const rel = relative7(cacheRoot, control.pluginParent), parts = rel.split(sep8);
16486
16692
  if (resolve14(control.pluginParent) !== control.pluginParent || parts.length !== 2 || parts.some((part) => !identifier3(part)) || control.pluginId !== `${control.namespace}@${parts[0]}` || parts[1] !== control.namespace)
16487
16693
  refuse4();
@@ -16522,7 +16728,9 @@ function assertParents(cacheRoot, controls, appOnlyParents, files, members) {
16522
16728
  refuse4();
16523
16729
  if ([".app.json", ".mcp.json"].includes(base) && (parts.length !== 4 || dirname15(member) !== root))
16524
16730
  refuse4();
16525
- if (parts.includes("hooks"))
16731
+ if ([".app.json", ".mcp.json"].includes(base) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
16732
+ refuse4();
16733
+ if ((parts[3] === "hooks" || base === "hooks.json") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base !== "hooks" : base !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join20(cacheRoot, parts[0], parts[1]))))
16526
16734
  refuse4();
16527
16735
  }
16528
16736
  }
@@ -16632,13 +16840,14 @@ function captureCodexSemanticCacheWitness(options) {
16632
16840
  const initialTree = treeSnapshot(options.cacheRoot);
16633
16841
  const controls = controlProjection(options.controls, read2);
16634
16842
  const appOnlyParents = appOnlyParentProjection(options.cacheRoot, options.appOnlyParents ?? [], read2);
16843
+ const hookParents = reviewedHookParentProjection(options.cacheRoot, options.reviewedHookParents ?? [], read2);
16635
16844
  const skillParents = new Set(controls.map((control) => control.pluginParent));
16636
- if (appOnlyParents.some((parent) => skillParents.has(parent.pluginParent)))
16845
+ if (appOnlyParents.some((parent) => skillParents.has(parent.pluginParent)) || hookParents.some((parent) => skillParents.has(parent.pluginParent) || appOnlyParents.some((app) => app.pluginParent === parent.pluginParent)))
16637
16846
  refuse4();
16638
16847
  if (!reviewedCodexPluginCapabilitiesUnchanged(options.cacheRoot, options.controls, read2, options.rules))
16639
16848
  refuse4();
16640
16849
  const files = initialTree.files;
16641
- assertParents(options.cacheRoot, controls, appOnlyParents, files, initialTree.state.members);
16850
+ assertParents(options.cacheRoot, controls, appOnlyParents, hookParents, files, initialTree.state.members);
16642
16851
  const expectedNamesByRoot = assertSkillMaterializations(options.cacheRoot, options.controls, files, read2);
16643
16852
  const docs = files.filter((path) => path.endsWith(`${sep8}SKILL.md`));
16644
16853
  const skillRows = [];
@@ -16647,6 +16856,8 @@ function captureCodexSemanticCacheWitness(options) {
16647
16856
  const rel = relative7(options.cacheRoot, document2).split(sep8);
16648
16857
  if (appOnlyParents.some((parent) => parent.pluginParent === join20(options.cacheRoot, rel[0], rel[1])))
16649
16858
  refuse4();
16859
+ if (hookParents.some((parent) => parent.pluginParent === join20(options.cacheRoot, rel[0], rel[1])))
16860
+ refuse4();
16650
16861
  const documentStat = lstatSync16(document2, { throwIfNoEntry: false });
16651
16862
  if (!documentStat?.isFile() || documentStat.isSymbolicLink() || documentStat.size > MAX_DOCUMENT_BYTES)
16652
16863
  refuse4();
@@ -16710,7 +16921,7 @@ function captureCodexSemanticCacheWitness(options) {
16710
16921
  tracked.verify();
16711
16922
  const finalTree = treeSnapshot(options.cacheRoot);
16712
16923
  assertSameTree(initialTree.state, finalTree.state, tracked.paths());
16713
- const completeProjection = { schema: CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA, cacheRoot: options.cacheRoot, controls, appOnlyParents, skills: uniqueSkills };
16924
+ const completeProjection = { schema: CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA, cacheRoot: options.cacheRoot, controls, appOnlyParents, hookParents, skills: uniqueSkills };
16714
16925
  return { ...completeProjection, sha256: hash2(completeProjection) };
16715
16926
  } catch (error) {
16716
16927
  if (error instanceof Error && error.message.startsWith("CODEX_SEMANTIC_CACHE_WITNESS_UNSUPPORTED"))
@@ -16726,6 +16937,7 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
16726
16937
  cacheRoot: previous.cacheRoot,
16727
16938
  controls: previous.controls,
16728
16939
  appOnlyParents: previous.appOnlyParents,
16940
+ ...previous.hookParents === undefined ? {} : { hookParents: previous.hookParents },
16729
16941
  skills: previous.skills
16730
16942
  };
16731
16943
  const currentProjection = {
@@ -16733,9 +16945,11 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
16733
16945
  cacheRoot: current.cacheRoot,
16734
16946
  controls: current.controls,
16735
16947
  appOnlyParents: current.appOnlyParents,
16948
+ ...current.hookParents === undefined ? {} : { hookParents: current.hookParents },
16736
16949
  skills: current.skills
16737
16950
  };
16738
- if (hash2(priorProjection) !== previous.sha256 || hash2(currentProjection) !== current.sha256 || canonical7(priorProjection) !== canonical7(currentProjection))
16951
+ const comparable = (projection) => ({ ...projection, hookParents: projection.hookParents ?? [] });
16952
+ if (hash2(priorProjection) !== previous.sha256 || hash2(currentProjection) !== current.sha256 || canonical7(comparable(priorProjection)) !== canonical7(comparable(currentProjection)))
16739
16953
  refuse4();
16740
16954
  }
16741
16955
 
@@ -16743,7 +16957,7 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
16743
16957
  import { createHash as createHash23, randomUUID as randomUUID3 } from "crypto";
16744
16958
  import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
16745
16959
  import { existsSync as existsSync9, lstatSync as lstatSync23, statSync as statSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync16, readdirSync as readdirSync9, opendirSync as opendirSync3, readlinkSync as readlinkSync7, realpathSync as realpathSync12, renameSync as renameSync3, rmdirSync as rmdirSync2, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4, chmodSync, openSync as openSync14, closeSync as closeSync14, fsyncSync as fsyncSync6, fstatSync as fstatSync14, readSync as readSync10, constants as constants15, linkSync as linkSync2 } from "fs";
16746
- import { dirname as dirname18, join as join27, resolve as resolve18, relative as relative8, isAbsolute as isAbsolute19, sep as sep10 } from "path";
16960
+ import { basename as basename9, dirname as dirname18, join as join27, resolve as resolve18, relative as relative8, isAbsolute as isAbsolute19, sep as sep10 } from "path";
16747
16961
  import { homedir as homedir2 } from "os";
16748
16962
 
16749
16963
  // src/lib/managed-policy.ts
@@ -19107,7 +19321,7 @@ function planAgentSettingsWitnessUpgrade(options) {
19107
19321
  return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join27(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join27(dataDir2, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], ...policy.bridge.discoveryExecutables ? { discoveryExecutables: policy.bridge.discoveryExecutables } : {}, rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
19108
19322
  }
19109
19323
  function planCodexSemanticCacheWitnessUpgrade(options) {
19110
- if (options.agent !== "codex" || !/^[a-f0-9]{64}$/.test(options.expectedPolicySha256) || !/^[a-f0-9]{64}$/.test(options.expectedSettingsSha256) || options.expectedCacheWitnessSha256 !== undefined && !/^[a-f0-9]{64}$/.test(options.expectedCacheWitnessSha256) || !Array.isArray(options.appOnlyParents) || options.appOnlyParents.length > 4096 || new Set(options.appOnlyParents).size !== options.appOnlyParents.length)
19324
+ if (options.agent !== "codex" || !/^[a-f0-9]{64}$/.test(options.expectedPolicySha256) || !/^[a-f0-9]{64}$/.test(options.expectedSettingsSha256) || options.expectedCacheWitnessSha256 !== undefined && !/^[a-f0-9]{64}$/.test(options.expectedCacheWitnessSha256) || !Array.isArray(options.appOnlyParents) || options.appOnlyParents.length > 4096 || new Set(options.appOnlyParents).size !== options.appOnlyParents.length || options.reviewedHookParents !== undefined && (!Array.isArray(options.reviewedHookParents) || options.reviewedHookParents.length > 4096 || new Set(options.reviewedHookParents).size !== options.reviewedHookParents.length))
19111
19325
  throw new Error("Invalid Codex semantic cache upgrade request");
19112
19326
  const home = resolve18(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
19113
19327
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
@@ -19131,14 +19345,23 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
19131
19345
  throw new Error("Native settings preimage changed");
19132
19346
  const boundedRead = (path) => new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024));
19133
19347
  const unchangedFullSource = (path) => {
19134
- const source = binding.sources.find((source2) => source2.path === path && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && source2.reviewArtifact === undefined && (source2.hashMode === undefined || source2.hashMode === "bytes"));
19135
- if (!source)
19136
- throw new Error("Cache upgrade lacks prior full identity source");
19348
+ const sources = binding.sources.filter((source2) => source2.path === path && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && source2.reviewArtifact === undefined && (source2.hashMode === undefined || source2.hashMode === "bytes"));
19349
+ if (sources.length !== 1)
19350
+ throw new Error("Cache upgrade requires one unambiguous prior full identity source");
19351
+ const source = sources[0];
19137
19352
  const text2 = boundedRead(path);
19138
19353
  if (sha4(text2) !== source.sha256)
19139
19354
  throw new Error("Reviewed cache identity source changed");
19140
19355
  return text2;
19141
19356
  };
19357
+ const unchangedReviewedHookSource = (path) => {
19358
+ const sources = binding.sources.filter((source) => source.path === path);
19359
+ if (!sources.length)
19360
+ throw new Error("Cache upgrade lacks prior full identity source");
19361
+ if (sources.length !== 1)
19362
+ throw new Error("Reviewed hook identity source is ambiguous");
19363
+ return unchangedFullSource(path);
19364
+ };
19142
19365
  const appOnlyParents = options.appOnlyParents.map((parent) => {
19143
19366
  const parts = relative8(cacheRoot, parent).split(sep10);
19144
19367
  if (!isAbsolute19(parent) || resolve18(parent) !== parent || parts.length !== 2 || parts.some((part) => !/^[A-Za-z0-9_-]{1,64}$/.test(part)) || controls.some((control) => control.pluginParent === parent))
@@ -19153,11 +19376,60 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
19153
19376
  unchangedFullSource(source.path);
19154
19377
  return { role: "app-only", pluginId: `${parts[1]}@${parts[0]}`, namespace: parts[1], pluginParent: parent, remotePluginId: receipt.remote_plugin_id };
19155
19378
  }).sort((a, b) => a.pluginParent.localeCompare(b.pluginParent));
19156
- const parents2 = [...new Set([...controls.map((control) => control.pluginParent), ...appOnlyParents.map((parent) => parent.pluginParent)])];
19379
+ const reviewedHookParents = (options.reviewedHookParents ?? []).map((parent) => {
19380
+ const parts = relative8(cacheRoot, parent).split(sep10);
19381
+ if (!isAbsolute19(parent) || resolve18(parent) !== parent || parts.length !== 2 || parts.some((part) => !/^[A-Za-z0-9_-]{1,64}$/.test(part)) || controls.some((control) => control.pluginParent === parent) || appOnlyParents.some((role) => role.pluginParent === parent))
19382
+ throw new Error("Unsupported reviewed native-hook parent role");
19383
+ const receiptPath = join27(parent, ".codex-remote-plugin-install.json"), receiptStat = lstatSync23(receiptPath, { throwIfNoEntry: false });
19384
+ let identity5;
19385
+ if (receiptStat) {
19386
+ if (!receiptStat.isFile() || receiptStat.isSymbolicLink() || receiptStat.size > 16 * 1024)
19387
+ throw new Error("Invalid reviewed native-hook install receipt");
19388
+ const receiptText = unchangedReviewedHookSource(receiptPath);
19389
+ let receipt;
19390
+ try {
19391
+ hashNativeJsonControls(receiptText);
19392
+ receipt = JSON.parse(receiptText);
19393
+ } catch {
19394
+ throw new Error("Invalid reviewed native-hook install receipt");
19395
+ }
19396
+ if (!receipt || Array.isArray(receipt) || Object.keys(receipt).length !== 2 || receipt.schema_version !== 1 || typeof receipt.remote_plugin_id !== "string" || !/^[A-Za-z0-9_~-]{1,1024}$/.test(receipt.remote_plugin_id))
19397
+ throw new Error("Invalid reviewed native-hook install receipt");
19398
+ identity5 = { role: "reviewed-hook", pluginId: `${parts[1]}@${parts[0]}`, namespace: parts[1], pluginParent: parent, installation: "remote-receipt", remotePluginId: receipt.remote_plugin_id };
19399
+ } else {
19400
+ const pluginId = `${parts[1]}@${parts[0]}`;
19401
+ const marketplaceSources = binding.sources.filter((source2) => basename9(source2.path) === "marketplace.json" && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && source2.reviewArtifact === undefined && (source2.hashMode === undefined || source2.hashMode === "bytes"));
19402
+ const matches = marketplaceSources.flatMap((source2) => {
19403
+ const text2 = unchangedFullSource(source2.path);
19404
+ return codexLocalMarketplaceMapsPlugin(text2, parts[0], parts[1]) ? [{ source: source2, text: text2 }] : [];
19405
+ });
19406
+ if (matches.length !== 1)
19407
+ throw new Error("Local marketplace hook parent lacks one exact enrolled marketplace source");
19408
+ const { source } = matches[0];
19409
+ const config = Bun.TOML.parse(settings);
19410
+ if (config.plugins?.[pluginId]?.enabled !== true)
19411
+ throw new Error("Local marketplace hook parent is not enabled in the unchanged Codex settings");
19412
+ identity5 = { role: "reviewed-hook", pluginId, namespace: parts[1], pluginParent: parent, installation: "local-marketplace", marketplaceSourcePath: source.path, marketplaceSha256: source.sha256 };
19413
+ }
19414
+ const versions = readdirSync9(parent).filter((name) => name !== ".codex-remote-plugin-install.json");
19415
+ if (!versions.length || versions.length > 4096)
19416
+ throw new Error("Reviewed native-hook parent has no bounded materializations");
19417
+ for (const version2 of versions) {
19418
+ if (!/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2))
19419
+ throw new Error("Invalid reviewed native-hook version identity");
19420
+ const root = join27(parent, version2), stat = lstatSync23(root, { throwIfNoEntry: false });
19421
+ if (!stat?.isDirectory() || stat.isSymbolicLink() || realpathSync12(root) !== root)
19422
+ throw new Error("Invalid reviewed native-hook materialization");
19423
+ unchangedReviewedHookSource(join27(root, ".codex-plugin/plugin.json"));
19424
+ unchangedReviewedHookSource(join27(root, "hooks/hooks.json"));
19425
+ }
19426
+ return identity5;
19427
+ }).sort((a, b) => a.pluginParent.localeCompare(b.pluginParent));
19428
+ const parents2 = [...new Set([...controls.map((control) => control.pluginParent), ...appOnlyParents.map((parent) => parent.pluginParent), ...reviewedHookParents.map((parent) => parent.pluginParent)])];
19157
19429
  const reviewedAlias = aliases.find((alias) => alias.agent === "codex");
19158
19430
  const hookRootAlias = reviewedAlias ? { alias: reviewedAlias.alias, target: reviewedAlias.target } : null;
19159
19431
  assertCodexSemanticCacheHookSafety(cacheRoot, parents2, configPath, hookRootAlias);
19160
- const supersededSources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents2));
19432
+ const supersededSources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents2, reviewedHookParents.map((parent) => parent.pluginParent)));
19161
19433
  for (const source of supersededSources)
19162
19434
  if ([".app.json", ".mcp.json"].includes(source.path.split(sep10).at(-1)))
19163
19435
  unchangedFullSource(source.path);
@@ -19165,7 +19437,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
19165
19437
  if (!supersededDirectories.some((directory) => directory.path === cacheRoot) || (binding.directories ?? []).some((directory) => cacheRoot.startsWith(directory.path + sep10)))
19166
19438
  throw new Error("Cache upgrade requires exact cache directory coverage without an unproved ancestor projection");
19167
19439
  const rules = Bun.TOML.parse(settings).skills?.config ?? [];
19168
- const witness = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents, rules, read: boundedRead });
19440
+ const witness = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents, reviewedHookParents, rules, read: boundedRead });
19169
19441
  if (options.expectedCacheWitnessSha256 !== undefined && witness.sha256 !== options.expectedCacheWitnessSha256)
19170
19442
  throw new Error("Reviewed semantic cache preimage changed");
19171
19443
  const replacement = { ...binding, codexDisabledPluginSkills: controls, codexSemanticCache: {
@@ -19174,6 +19446,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
19174
19446
  hookRootAlias,
19175
19447
  witness,
19176
19448
  appOnlyParents,
19449
+ reviewedHookParents,
19177
19450
  previousPolicySha256: options.expectedPolicySha256,
19178
19451
  supersededSources,
19179
19452
  supersededDirectories
@@ -19192,7 +19465,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
19192
19465
  rootAliases: aliases,
19193
19466
  managedAgentChecks: { home, agents: ["codex"] },
19194
19467
  ...policy.bridge.discoveryExecutables ? { discoveryExecutables: policy.bridge.discoveryExecutables } : {},
19195
- cacheWitnessUpgrade: { ...options, home, dataDir: dataDir2, appOnlyParents: appOnlyParents.map((parent) => parent.pluginParent) }
19468
+ cacheWitnessUpgrade: { ...options, home, dataDir: dataDir2, appOnlyParents: appOnlyParents.map((parent) => parent.pluginParent), reviewedHookParents: reviewedHookParents.map((parent) => parent.pluginParent) }
19196
19469
  };
19197
19470
  }
19198
19471
  function planReviewedArtifactMigration(options) {
@@ -19970,7 +20243,7 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
19970
20243
  return { action, path: skillMdPath };
19971
20244
  const parentDir = dirname19(dir);
19972
20245
  mkdirSync8(parentDir, { recursive: true });
19973
- const transactionDir = mkdtempSync(join28(parentDir, `.hasna-skills-write-${basename8(dir)}-`));
20246
+ const transactionDir = mkdtempSync(join28(parentDir, `.hasna-skills-write-${basename10(dir)}-`));
19974
20247
  const candidateDir = join28(transactionDir, "candidate");
19975
20248
  const backupDir = join28(transactionDir, "backup");
19976
20249
  const candidateSkillMdPath = join28(candidateDir, "SKILL.md");
@@ -20824,9 +21097,7 @@ var GENERIC_ENV_PATTERN = /\b((?:OPENAI|ANTHROPIC|GEMINI|XAI|ELEVENLABS|DEEPGRAM
20824
21097
  function extractEnvVars(text2) {
20825
21098
  const envVars = new Set;
20826
21099
  for (const pattern of [ENV_VAR_PATTERN, GENERIC_ENV_PATTERN]) {
20827
- pattern.lastIndex = 0;
20828
- let match;
20829
- while ((match = pattern.exec(text2)) !== null) {
21100
+ for (const match of text2.matchAll(pattern)) {
20830
21101
  envVars.add(match[1]);
20831
21102
  }
20832
21103
  }
@@ -22000,7 +22271,7 @@ function hashManagedPluginRegistry(path, managed) {
22000
22271
  pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join34(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
22001
22272
  const approval = (producer) => {
22002
22273
  pluginText(producer);
22003
- const digest2 = `sha256:${basename9(producer)}`;
22274
+ const digest2 = `sha256:${basename11(producer)}`;
22004
22275
  const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest2);
22005
22276
  pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
22006
22277
  if (!verified.has(producer)) {
@@ -22064,7 +22335,7 @@ function captureManagedPluginRegistry(path, managedPlugins) {
22064
22335
  // src/lib/claude-marketplace-registry.ts
22065
22336
  import { createHash as createHash26 } from "crypto";
22066
22337
  import { closeSync as closeSync17, constants as constants18, fstatSync as fstatSync17, lstatSync as lstatSync27, openSync as openSync17, readSync as readSync13 } from "fs";
22067
- import { basename as basename10, dirname as dirname22, isAbsolute as isAbsolute23, resolve as resolve22 } from "path";
22338
+ import { basename as basename12, dirname as dirname22, isAbsolute as isAbsolute23, resolve as resolve22 } from "path";
22068
22339
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
22069
22340
  function need8(value, reason2) {
22070
22341
  if (!value)
@@ -22086,7 +22357,7 @@ function parents2(path) {
22086
22357
  }
22087
22358
  function readRegistry2(path, budget2) {
22088
22359
  absolutePath4(path);
22089
- need8(basename10(path) === "known_marketplaces.json", "requires known_marketplaces.json");
22360
+ need8(basename12(path) === "known_marketplaces.json", "requires known_marketplaces.json");
22090
22361
  need8(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
22091
22362
  const ancestors2 = parents2(path), initial = lstatSync27(path, { bigint: true });
22092
22363
  need8(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
@@ -22367,7 +22638,7 @@ function assertReviewedClaudeCacheClosures(home, config, review, canonical9) {
22367
22638
  throw new Error("Reviewed Claude enabled plugin registration requires an object with an installPath string");
22368
22639
  }
22369
22640
  if ([cacheAliasRoot, cacheRoot].some((root2) => row.installPath === root2 || row.installPath.startsWith(root2 + sep11))) {
22370
- if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename11(row.installPath) !== row.version)) {
22641
+ if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename13(row.installPath) !== row.version)) {
22371
22642
  throw new Error("Reviewed Claude discovery has a malformed active cache registration");
22372
22643
  }
22373
22644
  if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute24(row.projectPath) && resolve23(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
@@ -22645,7 +22916,7 @@ function absentRetiredClaudeRoots(binding) {
22645
22916
  if (!targetValid || !targetScopes.has("user\x00"))
22646
22917
  continue;
22647
22918
  const active = rows[0]?.installPath;
22648
- if (typeof active !== "string" || rows[0]?.version !== basename11(active) || dirname23(active) !== dirname23(root2) || active === root2 || !lstatSync28(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync13(active) !== active)
22919
+ if (typeof active !== "string" || rows[0]?.version !== basename13(active) || dirname23(active) !== dirname23(root2) || active === root2 || !lstatSync28(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync13(active) !== active)
22649
22920
  continue;
22650
22921
  const activeManifest = full(join35(active, ".claude-plugin/plugin.json"));
22651
22922
  if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
@@ -22654,14 +22925,15 @@ function absentRetiredClaudeRoots(binding) {
22654
22925
  }
22655
22926
  return [...result];
22656
22927
  }
22657
- function codexSemanticCacheSourceRole(source, cacheRoot, parents3) {
22928
+ function codexSemanticCacheSourceRole(source, cacheRoot, parents3, hookParents = []) {
22658
22929
  if (!isAbsolute24(source.path) || resolve23(source.path) !== source.path || /[\x00-\x1f\x7f]/.test(source.path) || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.reviewArtifact !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes")
22659
22930
  return false;
22660
22931
  const parts = relative9(cacheRoot, source.path).split(sep11);
22661
22932
  if (parts.length < 3 || !parents3.includes(join35(cacheRoot, parts[0], parts[1])))
22662
22933
  return false;
22663
22934
  const tail = parts.slice(3).join("/");
22664
- return parts.length === 5 && tail === ".codex-plugin/plugin.json" && source.sha256 !== null || parts.length === 4 && [".app.json", ".mcp.json"].includes(tail) && source.sha256 !== null || source.sha256 === null && (parts.length === 4 && tail === "SKILL.md" || parts.length === 5 && tail === "hooks/hooks.json");
22935
+ const parent = join35(cacheRoot, parts[0], parts[1]);
22936
+ return parts.length === 5 && tail === ".codex-plugin/plugin.json" && source.sha256 !== null || parts.length === 4 && [".app.json", ".mcp.json"].includes(tail) && source.sha256 !== null || parts.length === 5 && tail === "hooks/hooks.json" && hookParents.includes(parent) && source.sha256 !== null || source.sha256 === null && (parts.length === 4 && tail === "SKILL.md" || parts.length === 5 && tail === "hooks/hooks.json");
22665
22937
  }
22666
22938
  function codexSemanticCacheDirectoryRole(path, cacheRoot) {
22667
22939
  return isAbsolute24(path) && resolve23(path) === path && !/[\x00-\x1f\x7f]/.test(path) && (path === cacheRoot || path.startsWith(cacheRoot + sep11));
@@ -22693,20 +22965,28 @@ function verifyCodexSemanticCacheDiscovery(binding, reviewedRootAliases) {
22693
22965
  const cacheRoot = proof.witness.cacheRoot;
22694
22966
  if (controls.some((control) => dirname23(dirname23(control.pluginParent)) !== cacheRoot))
22695
22967
  throw new Error("Codex semantic cache names another enrolled cache");
22696
- const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent)])];
22697
- const sources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3));
22968
+ const reviewedHookParents = proof.reviewedHookParents ?? [];
22969
+ if (!Array.isArray(reviewedHookParents) || reviewedHookParents.length > 4096 || reviewedHookParents.some((parent) => !parent || !(parent.installation === "remote-receipt" && Object.keys(parent).sort().join(",") === "installation,namespace,pluginId,pluginParent,remotePluginId,role" && typeof parent.remotePluginId === "string" || parent.installation === "local-marketplace" && Object.keys(parent).sort().join(",") === "installation,marketplaceSha256,marketplaceSourcePath,namespace,pluginId,pluginParent,role" && typeof parent.marketplaceSourcePath === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256 ?? "")) || parent.role !== "reviewed-hook" || typeof parent.pluginId !== "string" || typeof parent.namespace !== "string" || typeof parent.pluginParent !== "string") || new Set(reviewedHookParents.map((parent) => parent.pluginParent)).size !== reviewedHookParents.length || !isDeepStrictEqual7((proof.witness.hookParents ?? []).map(({ manifestSha256: _manifest, hooksSha256: _hooks, receiptSha256: _receipt, ...parent }) => parent), reviewedHookParents)) {
22970
+ throw new Error("Invalid reviewed native-hook cache role");
22971
+ }
22972
+ for (const parent of reviewedHookParents)
22973
+ if (parent.installation === "local-marketplace" && !binding.sources.some((source) => source.path === parent.marketplaceSourcePath && source.sha256 === parent.marketplaceSha256 && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.reviewArtifact === undefined && (source.hashMode === undefined || source.hashMode === "bytes")))
22974
+ throw new Error("Local marketplace hook parent lost its enrolled source witness");
22975
+ const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent), ...reviewedHookParents.map((parent) => parent.pluginParent)])];
22976
+ const hookParentPaths = reviewedHookParents.map((parent) => parent.pluginParent);
22977
+ const sources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3, hookParentPaths));
22698
22978
  const directories = (binding.directories ?? []).filter((directory) => codexSemanticCacheDirectoryRole(directory.path, cacheRoot));
22699
22979
  if (!directories.length || !isDeepStrictEqual7(sources, proof.supersededSources) || !isDeepStrictEqual7(directories, proof.supersededDirectories))
22700
22980
  throw new Error("Codex semantic cache provenance changed");
22701
22981
  const configPath = proof.configPath;
22702
- if (!isAbsolute24(configPath) || resolve23(configPath) !== configPath || basename11(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
22982
+ if (!isAbsolute24(configPath) || resolve23(configPath) !== configPath || basename13(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
22703
22983
  throw new Error("Invalid semantic cache configuration source");
22704
22984
  const configSource = binding.sources.find((source) => source.path === configPath && source.sha256 !== null && source.format === undefined && source.fields === undefined);
22705
22985
  if (!configSource || projected(configSource) !== configSource.sha256)
22706
22986
  throw new Error("Codex semantic cache requires unchanged reviewed configuration");
22707
22987
  assertCodexSemanticCacheHookSafety(cacheRoot, parents3, configPath, proof.hookRootAlias);
22708
22988
  const rules = parseConfig(read2(configPath), configPath, true).skills?.config ?? [];
22709
- const current = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents: proof.appOnlyParents, rules });
22989
+ const current = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents: proof.appOnlyParents, reviewedHookParents, rules });
22710
22990
  assertCodexSemanticCacheWitnessUnchanged(proof.witness, current);
22711
22991
  return proof;
22712
22992
  }
@@ -22790,7 +23070,7 @@ function codexInstallationRoots(binding) {
22790
23070
  const config = join35(dirname23(dirname23(cache)), "config.toml"), home = dirname23(dirname23(dirname23(cache)));
22791
23071
  const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
22792
23072
  const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join35(home, path));
22793
- if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep11) || input.sourceRoot.startsWith(root2 + sep11)) || binding.sources.some((source) => basename11(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep11)))))
23073
+ if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep11) || input.sourceRoot.startsWith(root2 + sep11)) || binding.sources.some((source) => basename13(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep11)))))
22794
23074
  throw new Error("Native installation input overlaps configuration or loading roots");
22795
23075
  if (proof.plugins.some((input) => dirname23(dirname23(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join35(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
22796
23076
  throw new Error("Missing native installation input source witness");
@@ -22802,13 +23082,13 @@ function codexInstallationRoots(binding) {
22802
23082
  });
22803
23083
  }
22804
23084
  function assertMarketplaceBinding(binding, source) {
22805
- if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename11(source.path) !== "plugin.json" || !source.path.endsWith(join35(sep11, ".claude-plugin", "plugin.json"))))
23085
+ if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename13(source.path) !== "plugin.json" || !source.path.endsWith(join35(sep11, ".claude-plugin", "plugin.json"))))
22806
23086
  throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
22807
- if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename11(source.path) !== "sumi.json"))
23087
+ if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename13(source.path) !== "sumi.json"))
22808
23088
  throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
22809
- if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename11(source.path) !== "config.toml"))
23089
+ if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename13(source.path) !== "config.toml"))
22810
23090
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
22811
- if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename11(source.path) !== "settings.json"))
23091
+ if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) && (binding.agent !== "claude" || binding.method !== "reviewed" || basename13(source.path) !== "settings.json"))
22812
23092
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
22813
23093
  if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
22814
23094
  throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
@@ -22926,7 +23206,7 @@ function verifyAutomaticDiscoveryClosure(binding, options) {
22926
23206
  const current = resolveAgentDiscovery({ home: options.home, agent: binding.agent, canonical: options.canonical, executable: runtime });
22927
23207
  if (JSON.stringify(current) === JSON.stringify(binding))
22928
23208
  return;
22929
- const reviewedWithRuntime = (binding.builtinNames?.length ?? 0) > 0 || binding.sources.some((source) => basename11(source.path) === "package.json");
23209
+ const reviewedWithRuntime = (binding.builtinNames?.length ?? 0) > 0 || binding.sources.some((source) => basename13(source.path) === "package.json");
22930
23210
  const otherwiseCurrent = current.method === binding.method && JSON.stringify(current.roots) === JSON.stringify(binding.roots) && JSON.stringify(current.directories) === JSON.stringify(binding.directories) && current.sources.every((source) => binding.sources.some((saved) => isDeepStrictEqual7(saved, source)));
22931
23211
  if (command && gap && runtime === null && reviewedWithRuntime && otherwiseCurrent)
22932
23212
  throw discoveryRootUnresolved(binding.agent, command, gap);
@@ -23069,7 +23349,7 @@ function resolveAgentDiscovery(options) {
23069
23349
  throw new Error("Discovery review must include the agent configuration source");
23070
23350
  const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical9(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical9(configPath), source)) : sources;
23071
23351
  const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
23072
- if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename11(source.path) !== "plugin.json" || !source.path.endsWith(join35(sep11, ".claude-plugin", "plugin.json")))
23352
+ if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename13(source.path) !== "plugin.json" || !source.path.endsWith(join35(sep11, ".claude-plugin", "plugin.json")))
23073
23353
  return source;
23074
23354
  const pluginRoot = dirname23(dirname23(source.path)), hooksPath = join35(pluginRoot, "hooks/hooks.json");
23075
23355
  const hookStat = lstatSync28(hooksPath, { throwIfNoEntry: false });
@@ -23235,7 +23515,7 @@ function physicalPath(path) {
23235
23515
  let cursor = resolve24(path);
23236
23516
  const suffix = [];
23237
23517
  while (!lstatSync29(cursor, { throwIfNoEntry: false })) {
23238
- suffix.unshift(basename12(cursor));
23518
+ suffix.unshift(basename14(cursor));
23239
23519
  const parent = dirname24(cursor);
23240
23520
  if (parent === cursor)
23241
23521
  return resolve24(path);
@@ -23454,7 +23734,7 @@ function physicalImportPath(path) {
23454
23734
  let ancestor = resolve25(path);
23455
23735
  const suffix = [];
23456
23736
  while (!existsSync14(ancestor)) {
23457
- suffix.unshift(basename13(ancestor));
23737
+ suffix.unshift(basename15(ancestor));
23458
23738
  ancestor = dirname25(ancestor);
23459
23739
  }
23460
23740
  return join37(realpathSync15(ancestor), ...suffix);
@@ -23479,8 +23759,8 @@ function portPortableSkill(sourcePath, options = {}) {
23479
23759
  const explicitName = options.name != null;
23480
23760
  const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
23481
23761
  if (isOfficialSkillName(skillName) && !options.allowShadow) {
23482
- const sourceSlug = safeNormalizeName(basename13(absoluteSource));
23483
- const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename13(absoluteSource)}')` : ""} matches a bundled official skill.`;
23762
+ const sourceSlug = safeNormalizeName(basename15(absoluteSource));
23763
+ const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename15(absoluteSource)}')` : ""} matches a bundled official skill.`;
23484
23764
  throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
23485
23765
  }
23486
23766
  const readOnlyDestination = join37(resolvePortableSkillsRoot(options, false), skillName);
@@ -50070,7 +50350,7 @@ async function reconcileSkillSession(input2, options = {}) {
50070
50350
  // src/lib/claude-prospective-review.ts
50071
50351
  import { createHash as createHash38 } from "crypto";
50072
50352
  import { lstatSync as lstatSync31, readFileSync as readFileSync31, readdirSync as readdirSync18, realpathSync as realpathSync17 } from "fs";
50073
- import { basename as basename14, dirname as dirname33, isAbsolute as isAbsolute29, join as join47, relative as relative15, resolve as resolve30, sep as sep18 } from "path";
50353
+ import { basename as basename16, dirname as dirname33, isAbsolute as isAbsolute29, join as join47, relative as relative15, resolve as resolve30, sep as sep18 } from "path";
50074
50354
  var MAX_CANDIDATE_FILES = 16384;
50075
50355
  var MAX_CANDIDATE_BYTES = 256 * 1024 * 1024;
50076
50356
  var sha7 = (data) => createHash38("sha256").update(data).digest("hex");