@hasna/skills 0.10.65 → 0.10.66

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/mcp.js CHANGED
@@ -23,7 +23,7 @@ var package_default;
23
23
  var init_package = __esm(() => {
24
24
  package_default = {
25
25
  name: "@hasna/skills",
26
- version: "0.10.65",
26
+ version: "0.10.66",
27
27
  description: "Skills library for AI coding agents",
28
28
  type: "module",
29
29
  bin: {
@@ -23089,8 +23089,8 @@ function assertAgentPolicyCollections(policy) {
23089
23089
  }
23090
23090
  }
23091
23091
  if (value.codexSemanticCache !== undefined) {
23092
- const proof = record2(value.codexSemanticCache, 8), witness = record2(proof.witness, 6);
23093
- 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));
23092
+ const proof = record2(value.codexSemanticCache, 9), witness = record2(proof.witness, 7);
23093
+ 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));
23094
23094
  text(proof.configPath);
23095
23095
  requireBound(isAbsolute5(proof.configPath) && resolve4(proof.configPath) === proof.configPath && basename5(proof.configPath) === "config.toml");
23096
23096
  text(witness.cacheRoot);
@@ -23109,6 +23109,20 @@ function assertAgentPolicyCollections(policy) {
23109
23109
  text(parent[key]);
23110
23110
  requireBound(isAbsolute5(parent.pluginParent) && resolve4(parent.pluginParent) === parent.pluginParent);
23111
23111
  }
23112
+ if (proof.reviewedHookParents !== undefined)
23113
+ for (const parent of array2(proof.reviewedHookParents, 4096)) {
23114
+ requireBound(object2(parent) && parent.role === "reviewed-hook" && (parent.installation === "remote-receipt" && Object.keys(parent).length === 6 || parent.installation === "local-marketplace" && Object.keys(parent).length === 7));
23115
+ 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");
23116
+ for (const key of ["pluginId", "namespace", "pluginParent", "installation"])
23117
+ text(parent[key]);
23118
+ if (parent.installation === "remote-receipt")
23119
+ text(parent.remotePluginId);
23120
+ else {
23121
+ text(parent.marketplaceSourcePath);
23122
+ requireBound(typeof parent.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256) && isAbsolute5(parent.marketplaceSourcePath) && resolve4(parent.marketplaceSourcePath) === parent.marketplaceSourcePath && basename5(parent.marketplaceSourcePath) === "marketplace.json");
23123
+ }
23124
+ requireBound(isAbsolute5(parent.pluginParent) && resolve4(parent.pluginParent) === parent.pluginParent);
23125
+ }
23112
23126
  for (const row of [...array2(witness.controls, 4096), ...array2(witness.appOnlyParents, 4096)]) {
23113
23127
  requireBound(object2(row));
23114
23128
  for (const key of ["pluginId", "namespace", "pluginParent"])
@@ -23117,6 +23131,26 @@ function assertAgentPolicyCollections(policy) {
23117
23131
  if (row[key] !== undefined)
23118
23132
  requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
23119
23133
  }
23134
+ if (witness.hookParents !== undefined)
23135
+ for (const row of array2(witness.hookParents, 4096)) {
23136
+ requireBound(object2(row) && row.role === "reviewed-hook" && (row.installation === "remote-receipt" && Object.keys(row).length === 9 || row.installation === "local-marketplace" && Object.keys(row).length === 9));
23137
+ 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");
23138
+ for (const key of ["pluginId", "namespace", "pluginParent", "installation"])
23139
+ text(row[key]);
23140
+ if (row.installation === "remote-receipt")
23141
+ text(row.remotePluginId);
23142
+ else {
23143
+ text(row.marketplaceSourcePath);
23144
+ requireBound(typeof row.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(row.marketplaceSha256) && isAbsolute5(row.marketplaceSourcePath) && resolve4(row.marketplaceSourcePath) === row.marketplaceSourcePath && basename5(row.marketplaceSourcePath) === "marketplace.json");
23145
+ }
23146
+ requireBound(isAbsolute5(row.pluginParent) && resolve4(row.pluginParent) === row.pluginParent);
23147
+ for (const key of ["manifestSha256", "hooksSha256"])
23148
+ requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
23149
+ if (row.installation === "remote-receipt")
23150
+ requireBound(typeof row.receiptSha256 === "string" && /^[a-f0-9]{64}$/.test(row.receiptSha256));
23151
+ else
23152
+ requireBound(row.receiptSha256 === undefined);
23153
+ }
23120
23154
  for (const skill of array2(witness.skills, 4096)) {
23121
23155
  requireBound(object2(skill));
23122
23156
  for (const key of ["name", "pluginId", "namespace", "pluginParent"])
@@ -33667,7 +33701,25 @@ var init_codex_settings_witness = __esm(() => {
33667
33701
  // src/lib/codex-semantic-cache-witness.ts
33668
33702
  import { createHash as createHash14 } from "crypto";
33669
33703
  import { constants as constants7, closeSync as closeSync10, fstatSync as fstatSync10, lstatSync as lstatSync13, openSync as openSync10, readSync as readSync5, readdirSync as readdirSync7, realpathSync as realpathSync6 } from "fs";
33670
- import { dirname as dirname13, isAbsolute as isAbsolute11, join as join19, relative as relative6, resolve as resolve12, sep as sep7 } from "path";
33704
+ import { basename as basename7, dirname as dirname13, isAbsolute as isAbsolute11, join as join19, relative as relative6, resolve as resolve12, sep as sep7 } from "path";
33705
+ function codexLocalMarketplaceMapsPlugin(text2, marketplace, plugin) {
33706
+ try {
33707
+ hashNativeJsonControls(text2);
33708
+ const value = JSON.parse(text2);
33709
+ if (!plainObject(value) || !exactKeys(value, ["interface", "name", "plugins"]) || value.name !== marketplace || !(typeof value.interface === "string" || Number.isSafeInteger(value.interface)) || !Array.isArray(value.plugins) || value.plugins.length < 1 || value.plugins.length > 4096)
33710
+ return false;
33711
+ const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
33712
+ if (rows.length !== 1)
33713
+ return false;
33714
+ const row = rows[0];
33715
+ 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("\\"))
33716
+ return false;
33717
+ const parts = row.source.path.split("/");
33718
+ 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;
33719
+ } catch {
33720
+ return false;
33721
+ }
33722
+ }
33671
33723
  function defaultRead(path) {
33672
33724
  const before = lstatSync13(path, { throwIfNoEntry: false, bigint: true });
33673
33725
  if (before === undefined)
@@ -33729,7 +33781,7 @@ function validPluginParent(cacheRoot, pluginParent, namespace, pluginId) {
33729
33781
  return parts.length === 2 && parts.every(identifier3) && parts[1] === namespace && pluginId === `${namespace}@${parts[0]}`;
33730
33782
  }
33731
33783
  function assertWitnessShape(value) {
33732
- if (!plainObject(value) || !exactKeys(value, ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256"]) || value.schema !== CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA || typeof value.cacheRoot !== "string" || !isAbsolute11(value.cacheRoot) || resolve12(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)
33784
+ if (!plainObject(value) || !exactKeys(value, ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256"], ["hookParents"]) || value.schema !== CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA || typeof value.cacheRoot !== "string" || !isAbsolute11(value.cacheRoot) || resolve12(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)
33733
33785
  refuse3();
33734
33786
  const witness = value;
33735
33787
  const controlParents = new Set;
@@ -33750,6 +33802,20 @@ function assertWitnessShape(value) {
33750
33802
  refuse3();
33751
33803
  appOnlyParentPaths.add(row.pluginParent);
33752
33804
  }
33805
+ const hookParentPaths = new Set;
33806
+ const hookParents = witness.hookParents === undefined ? [] : witness.hookParents;
33807
+ if (!Array.isArray(hookParents) || hookParents.length > 4096)
33808
+ refuse3();
33809
+ for (const candidate of hookParents) {
33810
+ if (!plainObject(candidate))
33811
+ refuse3();
33812
+ const row = candidate;
33813
+ const remote = row.installation === "remote-receipt";
33814
+ const local = row.installation === "local-marketplace";
33815
+ 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" || !isAbsolute11(row.marketplaceSourcePath) || resolve12(row.marketplaceSourcePath) !== row.marketplaceSourcePath || basename7(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))
33816
+ refuse3();
33817
+ hookParentPaths.add(row.pluginParent);
33818
+ }
33753
33819
  const skillNames = new Set;
33754
33820
  const qualifiedNames = new Set;
33755
33821
  for (const candidate of witness.skills) {
@@ -33769,6 +33835,7 @@ function assertWitnessShape(value) {
33769
33835
  cacheRoot: witness.cacheRoot,
33770
33836
  controls: witness.controls,
33771
33837
  appOnlyParents: witness.appOnlyParents,
33838
+ ...witness.hookParents === undefined ? {} : { hookParents: witness.hookParents },
33772
33839
  skills: witness.skills
33773
33840
  };
33774
33841
  if (hash3(projection) !== witness.sha256)
@@ -33959,10 +34026,128 @@ function appOnlyParentProjection(cacheRoot, parents2, read) {
33959
34026
  }
33960
34027
  return rows.sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.pluginParent.localeCompare(b.pluginParent));
33961
34028
  }
33962
- function assertParents(cacheRoot, controls, appOnlyParents, files, members2) {
34029
+ function assertJsonValue(value, depth = 0, budget2 = { members: 0 }) {
34030
+ if (depth > 32 || ++budget2.members > 4096)
34031
+ refuse3();
34032
+ if (value === null || typeof value === "string" || typeof value === "boolean")
34033
+ return;
34034
+ if (typeof value === "number" && Number.isFinite(value))
34035
+ return;
34036
+ if (Array.isArray(value)) {
34037
+ for (const item of value)
34038
+ assertJsonValue(item, depth + 1, budget2);
34039
+ return;
34040
+ }
34041
+ if (!plainObject(value))
34042
+ refuse3();
34043
+ for (const [key, item] of Object.entries(value)) {
34044
+ if (!key || /[\x00-\x1f\x7f]/.test(key))
34045
+ refuse3();
34046
+ assertJsonValue(item, depth + 1, budget2);
34047
+ }
34048
+ }
34049
+ function reviewedHookControls(root, read) {
34050
+ const manifestPath = join19(root, ".codex-plugin/plugin.json"), hooksPath = join19(root, "hooks/hooks.json");
34051
+ let manifest, manifestSha256, hooks, hooksSha256;
34052
+ try {
34053
+ const manifestText = read(manifestPath);
34054
+ manifestSha256 = hashNativeJsonControls(manifestText, "version");
34055
+ manifest = JSON.parse(manifestText);
34056
+ const hooksText = read(hooksPath);
34057
+ hashNativeJsonControls(hooksText);
34058
+ hooksSha256 = createHash14("sha256").update(hooksText).digest("hex");
34059
+ hooks = JSON.parse(hooksText);
34060
+ } catch {
34061
+ return refuse3();
34062
+ }
34063
+ if (!plainObject(manifest) || !exactKeys(manifest, ["name", "version", "hooks"], ["description"]) || !identifier3(manifest.name) || typeof manifest.version !== "string" || manifest.version !== basename7(root) || manifest.hooks !== "./hooks/hooks.json" || manifest.description !== undefined && (typeof manifest.description !== "string" || !manifest.description.trim() || manifest.description.length > 1024))
34064
+ refuse3();
34065
+ const manifestRecord = manifest;
34066
+ if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"]) || !plainObject(hooks.hooks))
34067
+ refuse3();
34068
+ const hookTable = hooks.hooks;
34069
+ const events = Object.entries(hookTable);
34070
+ if (!events.length || events.length > 32)
34071
+ refuse3();
34072
+ for (const [event, groups] of events) {
34073
+ if (!codexHookEvents.has(event) || !Array.isArray(groups) || !groups.length || groups.length > 32)
34074
+ refuse3();
34075
+ for (const group of groups) {
34076
+ if (!plainObject(group) || !exactKeys(group, ["hooks"]) || !Array.isArray(group.hooks) || !group.hooks.length || group.hooks.length > 32)
34077
+ refuse3();
34078
+ const handlers = group.hooks;
34079
+ for (const handler of handlers) {
34080
+ 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))
34081
+ refuse3();
34082
+ assertJsonValue(handler.input);
34083
+ }
34084
+ }
34085
+ }
34086
+ return { namespace: manifestRecord.name, manifestSha256, hooksSha256 };
34087
+ }
34088
+ function reviewedHookParentProjection(cacheRoot, parents2, read) {
34089
+ if (!Array.isArray(parents2) || parents2.length > 4096)
34090
+ refuse3();
34091
+ const seen = new Set, rows = [];
34092
+ for (const parent of parents2) {
34093
+ const remote = parent?.installation === "remote-receipt", local = parent?.installation === "local-marketplace";
34094
+ 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" || !isAbsolute11(parent.marketplaceSourcePath) || resolve12(parent.marketplaceSourcePath) !== parent.marketplaceSourcePath || basename7(parent.marketplaceSourcePath) !== "marketplace.json" || !digest(parent.marketplaceSha256)) || !remote && !local)
34095
+ refuse3();
34096
+ const parts = relative6(cacheRoot, parent.pluginParent).split(sep7);
34097
+ if (!isAbsolute11(parent.pluginParent) || resolve12(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))
34098
+ refuse3();
34099
+ seen.add(parent.pluginParent);
34100
+ const parentStat = lstatSync13(parent.pluginParent, { throwIfNoEntry: false });
34101
+ if (!parentStat?.isDirectory() || parentStat.isSymbolicLink() || realpathSync6(parent.pluginParent) !== parent.pluginParent)
34102
+ refuse3();
34103
+ const receiptPath = join19(parent.pluginParent, ".codex-remote-plugin-install.json"), receiptStat = lstatSync13(receiptPath, { throwIfNoEntry: false });
34104
+ let receiptText;
34105
+ if (remote) {
34106
+ if (!receiptStat?.isFile() || receiptStat.isSymbolicLink() || receiptStat.size > MAX_RECEIPT_BYTES)
34107
+ refuse3();
34108
+ try {
34109
+ receiptText = read(receiptPath);
34110
+ hashNativeJsonControls(receiptText);
34111
+ const receipt = JSON.parse(receiptText);
34112
+ if (!plainObject(receipt) || !exactKeys(receipt, ["schema_version", "remote_plugin_id"]) || receipt.schema_version !== 1 || receipt.remote_plugin_id !== parent.remotePluginId)
34113
+ refuse3();
34114
+ } catch {
34115
+ refuse3();
34116
+ }
34117
+ } else {
34118
+ const marketplaceSourcePath = parent.marketplaceSourcePath ?? refuse3();
34119
+ if (receiptStat !== undefined || !parent.marketplaceSha256)
34120
+ refuse3();
34121
+ let marketplaceText;
34122
+ try {
34123
+ marketplaceText = read(marketplaceSourcePath);
34124
+ } catch {
34125
+ return refuse3();
34126
+ }
34127
+ if (createHash14("sha256").update(marketplaceText).digest("hex") !== parent.marketplaceSha256 || !codexLocalMarketplaceMapsPlugin(marketplaceText, parts[0], parent.namespace))
34128
+ refuse3();
34129
+ }
34130
+ const materializations = readdirSync7(parent.pluginParent).filter((name) => name !== ".codex-remote-plugin-install.json").map((version2) => {
34131
+ if (!/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2))
34132
+ refuse3();
34133
+ const root = join19(parent.pluginParent, version2), stat = lstatSync13(root, { throwIfNoEntry: false });
34134
+ if (!stat?.isDirectory() || stat.isSymbolicLink() || realpathSync6(root) !== root)
34135
+ refuse3();
34136
+ const controls = reviewedHookControls(root, read);
34137
+ if (controls.namespace !== parent.namespace)
34138
+ refuse3();
34139
+ return { manifestSha256: controls.manifestSha256, hooksSha256: controls.hooksSha256 };
34140
+ });
34141
+ if (!materializations.length || materializations.some((item) => canonical7(item) !== canonical7(materializations[0])))
34142
+ refuse3();
34143
+ rows.push({ ...parent, ...receiptText === undefined ? {} : { receiptSha256: createHash14("sha256").update(receiptText).digest("hex") }, ...materializations[0] });
34144
+ }
34145
+ return rows.sort((a, b) => a.pluginId.localeCompare(b.pluginId) || a.pluginParent.localeCompare(b.pluginParent));
34146
+ }
34147
+ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files, members2) {
33963
34148
  const expected = new Set;
33964
34149
  const expectedMarkets = new Set;
33965
- for (const control of [...controls, ...appOnlyParents]) {
34150
+ for (const control of [...controls, ...appOnlyParents, ...hookParents]) {
33966
34151
  const rel = relative6(cacheRoot, control.pluginParent), parts = rel.split(sep7);
33967
34152
  if (resolve12(control.pluginParent) !== control.pluginParent || parts.length !== 2 || parts.some((part) => !identifier3(part)) || control.pluginId !== `${control.namespace}@${parts[0]}` || parts[1] !== control.namespace)
33968
34153
  refuse3();
@@ -34003,7 +34188,9 @@ function assertParents(cacheRoot, controls, appOnlyParents, files, members2) {
34003
34188
  refuse3();
34004
34189
  if ([".app.json", ".mcp.json"].includes(base) && (parts.length !== 4 || dirname13(member) !== root))
34005
34190
  refuse3();
34006
- if (parts.includes("hooks"))
34191
+ if ([".app.json", ".mcp.json"].includes(base) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
34192
+ refuse3();
34193
+ if (parts.includes("hooks") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base !== "hooks" : base !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join19(cacheRoot, parts[0], parts[1]))))
34007
34194
  refuse3();
34008
34195
  }
34009
34196
  }
@@ -34113,13 +34300,14 @@ function captureCodexSemanticCacheWitness(options) {
34113
34300
  const initialTree = treeSnapshot(options.cacheRoot);
34114
34301
  const controls = controlProjection(options.controls, read);
34115
34302
  const appOnlyParents = appOnlyParentProjection(options.cacheRoot, options.appOnlyParents ?? [], read);
34303
+ const hookParents = reviewedHookParentProjection(options.cacheRoot, options.reviewedHookParents ?? [], read);
34116
34304
  const skillParents = new Set(controls.map((control) => control.pluginParent));
34117
- if (appOnlyParents.some((parent) => skillParents.has(parent.pluginParent)))
34305
+ if (appOnlyParents.some((parent) => skillParents.has(parent.pluginParent)) || hookParents.some((parent) => skillParents.has(parent.pluginParent) || appOnlyParents.some((app) => app.pluginParent === parent.pluginParent)))
34118
34306
  refuse3();
34119
34307
  if (!reviewedCodexPluginCapabilitiesUnchanged(options.cacheRoot, options.controls, read, options.rules))
34120
34308
  refuse3();
34121
34309
  const files = initialTree.files;
34122
- assertParents(options.cacheRoot, controls, appOnlyParents, files, initialTree.state.members);
34310
+ assertParents(options.cacheRoot, controls, appOnlyParents, hookParents, files, initialTree.state.members);
34123
34311
  const expectedNamesByRoot = assertSkillMaterializations(options.cacheRoot, options.controls, files, read);
34124
34312
  const docs = files.filter((path) => path.endsWith(`${sep7}SKILL.md`));
34125
34313
  const skillRows = [];
@@ -34128,6 +34316,8 @@ function captureCodexSemanticCacheWitness(options) {
34128
34316
  const rel = relative6(options.cacheRoot, document2).split(sep7);
34129
34317
  if (appOnlyParents.some((parent) => parent.pluginParent === join19(options.cacheRoot, rel[0], rel[1])))
34130
34318
  refuse3();
34319
+ if (hookParents.some((parent) => parent.pluginParent === join19(options.cacheRoot, rel[0], rel[1])))
34320
+ refuse3();
34131
34321
  const documentStat = lstatSync13(document2, { throwIfNoEntry: false });
34132
34322
  if (!documentStat?.isFile() || documentStat.isSymbolicLink() || documentStat.size > MAX_DOCUMENT_BYTES)
34133
34323
  refuse3();
@@ -34191,7 +34381,7 @@ function captureCodexSemanticCacheWitness(options) {
34191
34381
  tracked.verify();
34192
34382
  const finalTree = treeSnapshot(options.cacheRoot);
34193
34383
  assertSameTree(initialTree.state, finalTree.state, tracked.paths());
34194
- const completeProjection = { schema: CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA, cacheRoot: options.cacheRoot, controls, appOnlyParents, skills: uniqueSkills };
34384
+ const completeProjection = { schema: CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA, cacheRoot: options.cacheRoot, controls, appOnlyParents, hookParents, skills: uniqueSkills };
34195
34385
  return { ...completeProjection, sha256: hash3(completeProjection) };
34196
34386
  } catch (error61) {
34197
34387
  if (error61 instanceof Error && error61.message.startsWith("CODEX_SEMANTIC_CACHE_WITNESS_UNSUPPORTED"))
@@ -34207,6 +34397,7 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
34207
34397
  cacheRoot: previous.cacheRoot,
34208
34398
  controls: previous.controls,
34209
34399
  appOnlyParents: previous.appOnlyParents,
34400
+ ...previous.hookParents === undefined ? {} : { hookParents: previous.hookParents },
34210
34401
  skills: previous.skills
34211
34402
  };
34212
34403
  const currentProjection = {
@@ -34214,20 +34405,41 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
34214
34405
  cacheRoot: current.cacheRoot,
34215
34406
  controls: current.controls,
34216
34407
  appOnlyParents: current.appOnlyParents,
34408
+ ...current.hookParents === undefined ? {} : { hookParents: current.hookParents },
34217
34409
  skills: current.skills
34218
34410
  };
34219
- if (hash3(priorProjection) !== previous.sha256 || hash3(currentProjection) !== current.sha256 || canonical7(priorProjection) !== canonical7(currentProjection))
34411
+ const comparable = (projection) => ({ ...projection, hookParents: projection.hookParents ?? [] });
34412
+ if (hash3(priorProjection) !== previous.sha256 || hash3(currentProjection) !== current.sha256 || canonical7(comparable(priorProjection)) !== canonical7(comparable(currentProjection)))
34220
34413
  refuse3();
34221
34414
  }
34222
34415
  var CODEX_SEMANTIC_CACHE_WITNESS_SCHEMA = "hasna.codex-semantic-cache-witness.v1", identifier3 = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(value), digest = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value), refuse3 = () => {
34223
34416
  throw new Error("CODEX_SEMANTIC_CACHE_WITNESS_UNSUPPORTED: review Codex plugin cache identity and controls");
34224
- }, MAX_DOCUMENT_BYTES, MAX_RECEIPT_BYTES;
34417
+ }, MAX_DOCUMENT_BYTES, MAX_RECEIPT_BYTES, codexHookEvents;
34225
34418
  var init_codex_semantic_cache_witness = __esm(() => {
34226
34419
  init_claude_settings_witness();
34227
34420
  init_codex_plugin_skill_controls();
34228
34421
  init_codex_native_skill_catalog();
34229
34422
  MAX_DOCUMENT_BYTES = 1024 * 1024;
34230
34423
  MAX_RECEIPT_BYTES = 16 * 1024;
34424
+ codexHookEvents = new Set([
34425
+ "SessionStart",
34426
+ "UserPromptSubmit",
34427
+ "PreToolUse",
34428
+ "PostToolUse",
34429
+ "PostToolUseFailure",
34430
+ "Stop",
34431
+ "SubagentStart",
34432
+ "SubagentStop",
34433
+ "PreCompact",
34434
+ "Notification",
34435
+ "PermissionRequest",
34436
+ "SessionEnd",
34437
+ "ConfigChange",
34438
+ "WorktreeCreate",
34439
+ "WorktreeRemove",
34440
+ "TeammateIdle",
34441
+ "TaskComplete"
34442
+ ]);
34231
34443
  });
34232
34444
 
34233
34445
  // src/lib/managed-policy.ts
@@ -35978,7 +36190,7 @@ var init_plugin_admission = __esm(() => {
35978
36190
 
35979
36191
  // src/lib/plugin-discovery.ts
35980
36192
  import { closeSync as closeSync15, constants as constants13, fstatSync as fstatSync15, lstatSync as lstatSync19, openSync as openSync15, opendirSync as opendirSync3, readSync as readSync10 } from "fs";
35981
- import { basename as basename7, dirname as dirname17, isAbsolute as isAbsolute17, join as join28, resolve as resolve18 } from "path";
36193
+ import { basename as basename8, dirname as dirname17, isAbsolute as isAbsolute17, join as join28, resolve as resolve18 } from "path";
35982
36194
  function validateManagedPluginWitnesses(value) {
35983
36195
  pluginNeed(Array.isArray(value) && value.length > 0 && value.length <= PLUGIN_PROJECTION_LIMITS.registrations, "Invalid managed plugin witness collection");
35984
36196
  const seen = new Set;
@@ -36061,7 +36273,7 @@ function hashManagedPluginRegistry(path, managed) {
36061
36273
  pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join28(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
36062
36274
  const approval = (producer) => {
36063
36275
  pluginText(producer);
36064
- const digest2 = `sha256:${basename7(producer)}`;
36276
+ const digest2 = `sha256:${basename8(producer)}`;
36065
36277
  const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest2);
36066
36278
  pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
36067
36279
  if (!verified.has(producer)) {
@@ -36127,7 +36339,7 @@ var init_plugin_discovery = __esm(() => {
36127
36339
  // src/lib/claude-marketplace-registry.ts
36128
36340
  import { createHash as createHash19 } from "crypto";
36129
36341
  import { closeSync as closeSync16, constants as constants14, fstatSync as fstatSync16, lstatSync as lstatSync20, openSync as openSync16, readSync as readSync11 } from "fs";
36130
- import { basename as basename8, dirname as dirname18, isAbsolute as isAbsolute18, resolve as resolve19 } from "path";
36342
+ import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute18, resolve as resolve19 } from "path";
36131
36343
  function need8(value, reason2) {
36132
36344
  if (!value)
36133
36345
  throw new Error(`Claude marketplace registry ${reason2}`);
@@ -36147,7 +36359,7 @@ function parents2(path) {
36147
36359
  }
36148
36360
  function readRegistry2(path, budget2) {
36149
36361
  absolutePath4(path);
36150
- need8(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
36362
+ need8(basename9(path) === "known_marketplaces.json", "requires known_marketplaces.json");
36151
36363
  need8(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
36152
36364
  const ancestors2 = parents2(path), initial = lstatSync20(path, { bigint: true });
36153
36365
  need8(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
@@ -36355,7 +36567,7 @@ var init_codex_hook_discovery_recovery = __esm(() => {
36355
36567
  // src/lib/agent-discovery.ts
36356
36568
  import { createHash as createHash21 } from "crypto";
36357
36569
  import { lstatSync as lstatSync21, readFileSync as readFileSync16, readdirSync as readdirSync8, realpathSync as realpathSync8 } from "fs";
36358
- import { basename as basename9, dirname as dirname19, isAbsolute as isAbsolute19, join as join29, relative as relative7, resolve as resolve20, sep as sep9 } from "path";
36570
+ import { basename as basename10, dirname as dirname19, isAbsolute as isAbsolute19, join as join29, relative as relative7, resolve as resolve20, sep as sep9 } from "path";
36359
36571
  import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
36360
36572
  function discoveryExecutable(agent) {
36361
36573
  const command = DISCOVERY_RUNTIME_COMMANDS[agent];
@@ -36422,7 +36634,7 @@ function assertReviewedClaudeCacheClosures(home, config2, review, canonical9) {
36422
36634
  throw new Error("Reviewed Claude enabled plugin registration requires an object with an installPath string");
36423
36635
  }
36424
36636
  if ([cacheAliasRoot, cacheRoot].some((root) => row.installPath === root || row.installPath.startsWith(root + sep9))) {
36425
- if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename9(row.installPath) !== row.version)) {
36637
+ if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename10(row.installPath) !== row.version)) {
36426
36638
  throw new Error("Reviewed Claude discovery has a malformed active cache registration");
36427
36639
  }
36428
36640
  if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute19(row.projectPath) && resolve20(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
@@ -36684,7 +36896,7 @@ function absentRetiredClaudeRoots(binding) {
36684
36896
  if (!targetValid || !targetScopes.has("user\x00"))
36685
36897
  continue;
36686
36898
  const active = rows[0]?.installPath;
36687
- if (typeof active !== "string" || rows[0]?.version !== basename9(active) || dirname19(active) !== dirname19(root) || active === root || !lstatSync21(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync8(active) !== active)
36899
+ if (typeof active !== "string" || rows[0]?.version !== basename10(active) || dirname19(active) !== dirname19(root) || active === root || !lstatSync21(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync8(active) !== active)
36688
36900
  continue;
36689
36901
  const activeManifest = full(join29(active, ".claude-plugin/plugin.json"));
36690
36902
  if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
@@ -36693,14 +36905,15 @@ function absentRetiredClaudeRoots(binding) {
36693
36905
  }
36694
36906
  return [...result];
36695
36907
  }
36696
- function codexSemanticCacheSourceRole(source, cacheRoot, parents3) {
36908
+ function codexSemanticCacheSourceRole(source, cacheRoot, parents3, hookParents = []) {
36697
36909
  if (!isAbsolute19(source.path) || resolve20(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")
36698
36910
  return false;
36699
36911
  const parts = relative7(cacheRoot, source.path).split(sep9);
36700
36912
  if (parts.length < 3 || !parents3.includes(join29(cacheRoot, parts[0], parts[1])))
36701
36913
  return false;
36702
36914
  const tail = parts.slice(3).join("/");
36703
- 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");
36915
+ const parent = join29(cacheRoot, parts[0], parts[1]);
36916
+ 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");
36704
36917
  }
36705
36918
  function codexSemanticCacheDirectoryRole(path, cacheRoot) {
36706
36919
  return isAbsolute19(path) && resolve20(path) === path && !/[\x00-\x1f\x7f]/.test(path) && (path === cacheRoot || path.startsWith(cacheRoot + sep9));
@@ -36732,20 +36945,28 @@ function verifyCodexSemanticCacheDiscovery(binding, reviewedRootAliases) {
36732
36945
  const cacheRoot = proof.witness.cacheRoot;
36733
36946
  if (controls.some((control) => dirname19(dirname19(control.pluginParent)) !== cacheRoot))
36734
36947
  throw new Error("Codex semantic cache names another enrolled cache");
36735
- const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent)])];
36736
- const sources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3));
36948
+ const reviewedHookParents = proof.reviewedHookParents ?? [];
36949
+ 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 || !isDeepStrictEqual4((proof.witness.hookParents ?? []).map(({ manifestSha256: _manifest, hooksSha256: _hooks, receiptSha256: _receipt, ...parent }) => parent), reviewedHookParents)) {
36950
+ throw new Error("Invalid reviewed native-hook cache role");
36951
+ }
36952
+ for (const parent of reviewedHookParents)
36953
+ 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")))
36954
+ throw new Error("Local marketplace hook parent lost its enrolled source witness");
36955
+ const parents3 = [...new Set([...controls.map((control) => control.pluginParent), ...proof.appOnlyParents.map((parent) => parent.pluginParent), ...reviewedHookParents.map((parent) => parent.pluginParent)])];
36956
+ const hookParentPaths = reviewedHookParents.map((parent) => parent.pluginParent);
36957
+ const sources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents3, hookParentPaths));
36737
36958
  const directories = (binding.directories ?? []).filter((directory) => codexSemanticCacheDirectoryRole(directory.path, cacheRoot));
36738
36959
  if (!directories.length || !isDeepStrictEqual4(sources, proof.supersededSources) || !isDeepStrictEqual4(directories, proof.supersededDirectories))
36739
36960
  throw new Error("Codex semantic cache provenance changed");
36740
36961
  const configPath = proof.configPath;
36741
- if (!isAbsolute19(configPath) || resolve20(configPath) !== configPath || basename9(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
36962
+ if (!isAbsolute19(configPath) || resolve20(configPath) !== configPath || basename10(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
36742
36963
  throw new Error("Invalid semantic cache configuration source");
36743
36964
  const configSource = binding.sources.find((source) => source.path === configPath && source.sha256 !== null && source.format === undefined && source.fields === undefined);
36744
36965
  if (!configSource || projected(configSource) !== configSource.sha256)
36745
36966
  throw new Error("Codex semantic cache requires unchanged reviewed configuration");
36746
36967
  assertCodexSemanticCacheHookSafety(cacheRoot, parents3, configPath, proof.hookRootAlias);
36747
36968
  const rules = parseConfig(read(configPath), configPath, true).skills?.config ?? [];
36748
- const current = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents: proof.appOnlyParents, rules });
36969
+ const current = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents: proof.appOnlyParents, reviewedHookParents, rules });
36749
36970
  assertCodexSemanticCacheWitnessUnchanged(proof.witness, current);
36750
36971
  return proof;
36751
36972
  }
@@ -36829,7 +37050,7 @@ function codexInstallationRoots(binding) {
36829
37050
  const config2 = join29(dirname19(dirname19(cache)), "config.toml"), home = dirname19(dirname19(dirname19(cache)));
36830
37051
  const configSource = binding.sources.find((source) => source.path === config2 && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
36831
37052
  const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join29(home, path));
36832
- if (!configSource || proof.plugins.some((input2) => nativeRoots.some((root) => root === input2.sourceRoot || root.startsWith(input2.sourceRoot + sep9) || input2.sourceRoot.startsWith(root + sep9)) || binding.sources.some((source) => basename9(source.path) === "config.toml" && (source.path === input2.sourceRoot || source.path.startsWith(input2.sourceRoot + sep9)))))
37053
+ if (!configSource || proof.plugins.some((input2) => nativeRoots.some((root) => root === input2.sourceRoot || root.startsWith(input2.sourceRoot + sep9) || input2.sourceRoot.startsWith(root + sep9)) || binding.sources.some((source) => basename10(source.path) === "config.toml" && (source.path === input2.sourceRoot || source.path.startsWith(input2.sourceRoot + sep9)))))
36833
37054
  throw new Error("Native installation input overlaps configuration or loading roots");
36834
37055
  if (proof.plugins.some((input2) => dirname19(dirname19(input2.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join29(input2.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
36835
37056
  throw new Error("Missing native installation input source witness");
@@ -36841,13 +37062,13 @@ function codexInstallationRoots(binding) {
36841
37062
  });
36842
37063
  }
36843
37064
  function assertMarketplaceBinding(binding, source) {
36844
- if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename9(source.path) !== "plugin.json" || !source.path.endsWith(join29(sep9, ".claude-plugin", "plugin.json"))))
37065
+ if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename10(source.path) !== "plugin.json" || !source.path.endsWith(join29(sep9, ".claude-plugin", "plugin.json"))))
36845
37066
  throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
36846
- if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename9(source.path) !== "sumi.json"))
37067
+ if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename10(source.path) !== "sumi.json"))
36847
37068
  throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
36848
- 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" || basename9(source.path) !== "config.toml"))
37069
+ 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" || basename10(source.path) !== "config.toml"))
36849
37070
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
36850
- 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" || basename9(source.path) !== "settings.json"))
37071
+ 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" || basename10(source.path) !== "settings.json"))
36851
37072
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
36852
37073
  if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
36853
37074
  throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
@@ -36996,7 +37217,7 @@ function resolveAgentDiscovery(options) {
36996
37217
  throw new Error("Discovery review must include the agent configuration source");
36997
37218
  const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical9(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical9(configPath), source)) : sources;
36998
37219
  const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
36999
- if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename9(source.path) !== "plugin.json" || !source.path.endsWith(join29(sep9, ".claude-plugin", "plugin.json")))
37220
+ if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename10(source.path) !== "plugin.json" || !source.path.endsWith(join29(sep9, ".claude-plugin", "plugin.json")))
37000
37221
  return source;
37001
37222
  const pluginRoot = dirname19(dirname19(source.path)), hooksPath = join29(pluginRoot, "hooks/hooks.json");
37002
37223
  const hookStat = lstatSync21(hooksPath, { throwIfNoEntry: false });
@@ -37176,7 +37397,7 @@ var init_agent_discovery = __esm(() => {
37176
37397
  // src/lib/authoring-path.ts
37177
37398
  import { lstatSync as lstatSync22, realpathSync as realpathSync9 } from "fs";
37178
37399
  import { homedir as homedir3 } from "os";
37179
- import { basename as basename10, dirname as dirname20, join as join30, resolve as resolve21, sep as sep10 } from "path";
37400
+ import { basename as basename11, dirname as dirname20, join as join30, resolve as resolve21, sep as sep10 } from "path";
37180
37401
  function pathParts(path) {
37181
37402
  return resolve21(path).split(sep10).filter(Boolean);
37182
37403
  }
@@ -37195,7 +37416,7 @@ function physicalPath(path) {
37195
37416
  let cursor = resolve21(path);
37196
37417
  const suffix = [];
37197
37418
  while (!lstatSync22(cursor, { throwIfNoEntry: false })) {
37198
- suffix.unshift(basename10(cursor));
37419
+ suffix.unshift(basename11(cursor));
37199
37420
  const parent = dirname20(cursor);
37200
37421
  if (parent === cursor)
37201
37422
  return resolve21(path);
@@ -37290,7 +37511,7 @@ import {
37290
37511
  statSync as statSync8,
37291
37512
  writeFileSync as writeFileSync6
37292
37513
  } from "fs";
37293
- import { basename as basename11, dirname as dirname21, isAbsolute as isAbsolute20, join as join31, normalize as normalize4, relative as relative8, resolve as resolve22, sep as sep11 } from "path";
37514
+ import { basename as basename12, dirname as dirname21, isAbsolute as isAbsolute20, join as join31, normalize as normalize4, relative as relative8, resolve as resolve22, sep as sep11 } from "path";
37294
37515
  function getPortableSkillsRoot(options = {}) {
37295
37516
  return resolvePortableSkillsRoot(options, true);
37296
37517
  }
@@ -37388,7 +37609,7 @@ function physicalImportPath(path) {
37388
37609
  let ancestor = resolve22(path);
37389
37610
  const suffix = [];
37390
37611
  while (!existsSync12(ancestor)) {
37391
- suffix.unshift(basename11(ancestor));
37612
+ suffix.unshift(basename12(ancestor));
37392
37613
  ancestor = dirname21(ancestor);
37393
37614
  }
37394
37615
  return join31(realpathSync10(ancestor), ...suffix);
@@ -37413,8 +37634,8 @@ function portPortableSkill(sourcePath, options = {}) {
37413
37634
  const explicitName = options.name != null;
37414
37635
  const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
37415
37636
  if (isOfficialSkillName(skillName) && !options.allowShadow) {
37416
- const sourceSlug = safeNormalizeName(basename11(absoluteSource));
37417
- const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename11(absoluteSource)}')` : ""} matches a bundled official skill.`;
37637
+ const sourceSlug = safeNormalizeName(basename12(absoluteSource));
37638
+ const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename12(absoluteSource)}')` : ""} matches a bundled official skill.`;
37418
37639
  throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
37419
37640
  }
37420
37641
  const readOnlyDestination = join31(resolvePortableSkillsRoot(options, false), skillName);
@@ -39043,13 +39264,13 @@ var init_remote_workspace_leave = __esm(() => {
39043
39264
 
39044
39265
  // src/lib/auth-store.ts
39045
39266
  import { chmodSync, existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync18, renameSync as renameSync4, statSync as statSync9, unlinkSync as unlinkSync4, writeFileSync as writeFileSync7 } from "fs";
39046
- import { basename as basename12, dirname as dirname22, join as join33 } from "path";
39267
+ import { basename as basename13, dirname as dirname22, join as join33 } from "path";
39047
39268
  function getAuthFilePath(env = process.env) {
39048
39269
  return skillsCredentialFilePath(env);
39049
39270
  }
39050
39271
  function getIdentityFilePath(env = process.env) {
39051
39272
  const file2 = skillsCredentialFilePath(env);
39052
- return join33(dirname22(file2), basename12(file2).replace(/^credentials/, "identity") + ".json");
39273
+ return join33(dirname22(file2), basename13(file2).replace(/^credentials/, "identity") + ".json");
39053
39274
  }
39054
39275
  function getApiUrl(action, env = process.env, options = {}) {
39055
39276
  return requireSkillsApiOrigin(action, env, options);
package/bin/migrate.js CHANGED
@@ -25,7 +25,7 @@ import { join as join5 } from "path";
25
25
  // package.json
26
26
  var package_default = {
27
27
  name: "@hasna/skills",
28
- version: "0.10.65",
28
+ version: "0.10.66",
29
29
  description: "Skills library for AI coding agents",
30
30
  type: "module",
31
31
  bin: {
package/bin/server.js CHANGED
@@ -24140,7 +24140,7 @@ restoreLaunchCwd();
24140
24140
  // package.json
24141
24141
  var package_default = {
24142
24142
  name: "@hasna/skills",
24143
- version: "0.10.65",
24143
+ version: "0.10.66",
24144
24144
  description: "Skills library for AI coding agents",
24145
24145
  type: "module",
24146
24146
  bin: {
package/bin/worker.js CHANGED
@@ -24143,7 +24143,7 @@ import { randomUUID as randomUUID6 } from "crypto";
24143
24143
  // package.json
24144
24144
  var package_default = {
24145
24145
  name: "@hasna/skills",
24146
- version: "0.10.65",
24146
+ version: "0.10.66",
24147
24147
  description: "Skills library for AI coding agents",
24148
24148
  type: "module",
24149
24149
  bin: {