@hasna/skills 0.10.34 → 0.10.36

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
@@ -9655,8 +9655,8 @@ function hashDiscoveryPathFile(path, budget, changes) {
9655
9655
  }
9656
9656
 
9657
9657
  // src/lib/plugin-discovery.ts
9658
- import { closeSync as closeSync10, constants as constants9, fstatSync as fstatSync10, lstatSync as lstatSync19, openSync as openSync10, opendirSync as opendirSync5, readSync as readSync8 } from "fs";
9659
- import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute14, join as join27, resolve as resolve14 } from "path";
9658
+ import { closeSync as closeSync11, constants as constants10, fstatSync as fstatSync11, lstatSync as lstatSync20, openSync as openSync11, opendirSync as opendirSync5, readSync as readSync9 } from "fs";
9659
+ import { basename as basename7, dirname as dirname15, isAbsolute as isAbsolute14, join as join27, resolve as resolve14 } from "path";
9660
9660
 
9661
9661
  // src/lib/plugin-admission.ts
9662
9662
  import { homedir as homedir5 } from "os";
@@ -10975,7 +10975,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
10975
10975
  // package.json
10976
10976
  var package_default = {
10977
10977
  name: "@hasna/skills",
10978
- version: "0.10.34",
10978
+ version: "0.10.36",
10979
10979
  description: "Skills library for AI coding agents",
10980
10980
  type: "module",
10981
10981
  bin: {
@@ -12797,7 +12797,7 @@ import { homedir as homedir4 } from "os";
12797
12797
  import {
12798
12798
  cpSync as cpSync2,
12799
12799
  existsSync as existsSync10,
12800
- lstatSync as lstatSync17,
12800
+ lstatSync as lstatSync18,
12801
12801
  mkdirSync as mkdirSync6,
12802
12802
  mkdtempSync,
12803
12803
  readFileSync as readFileSync13,
@@ -12805,10 +12805,10 @@ import {
12805
12805
  renameSync as renameSync3,
12806
12806
  rmSync,
12807
12807
  statSync as statSync9,
12808
- writeFileSync as writeFileSync5
12808
+ writeFileSync as writeFileSync6
12809
12809
  } from "fs";
12810
12810
  import { homedir as homedir3 } from "os";
12811
- import { basename as basename6, dirname as dirname12, join as join19 } from "path";
12811
+ import { basename as basename6, dirname as dirname13, join as join19 } from "path";
12812
12812
 
12813
12813
  // src/lib/codex-local-mcp-controls.ts
12814
12814
  function isCodexLocalStdioMcpServer(name, server) {
@@ -13717,7 +13717,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2) {
13717
13717
  return false;
13718
13718
  }
13719
13719
  }
13720
- function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules, pluginSettings) {
13720
+ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules, pluginSettings, plannedRules) {
13721
13721
  if (!supportsCodexNativeCapability(catalog?.version, "qualified-skill-catalog") || catalog.cwd !== cwd)
13722
13722
  refuse2();
13723
13723
  const skills = projectCodexNativeSkillCatalog({ data: [{ cwd, errors: [], skills: catalog.skills }] }, cwd);
@@ -13788,7 +13788,9 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
13788
13788
  remotePluginId = plugin.remotePluginId;
13789
13789
  }
13790
13790
  const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
13791
- if (!exactPathDeny || !isCodexNativeSkillDisabled({ name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true }, rules))
13791
+ const nativeSkill = { name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true };
13792
+ const plannedPathDeny = catalog.version === "codex-cli 0.160.0" && remotePluginId !== undefined && Array.isArray(rules) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === parsed.name && rule.enabled === false) && !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document2 && rule.enabled === false) && isCodexNativeSkillDisabled(nativeSkill, plannedRules);
13793
+ if (!exactPathDeny && !plannedPathDeny || !isCodexNativeSkillDisabled(nativeSkill, rules))
13792
13794
  refuse2();
13793
13795
  }
13794
13796
  result.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {} });
@@ -14208,10 +14210,10 @@ function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
14208
14210
  }
14209
14211
 
14210
14212
  // src/lib/agent-integration.ts
14211
- import { createHash as createHash12, randomUUID as randomUUID2 } from "crypto";
14213
+ import { createHash as createHash13, randomUUID as randomUUID2 } from "crypto";
14212
14214
  import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
14213
- import { existsSync as existsSync9, lstatSync as lstatSync16, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync12, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync4, realpathSync as realpathSync7, renameSync as renameSync2, rmdirSync as rmdirSync2, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, chmodSync, openSync as openSync8, closeSync as closeSync8, fsyncSync as fsyncSync2, fstatSync as fstatSync8, readSync as readSync6, constants as constants7, linkSync as linkSync2 } from "fs";
14214
- import { dirname as dirname11, join as join18, resolve as resolve11, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
14215
+ import { existsSync as existsSync9, lstatSync as lstatSync17, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync12, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync4, realpathSync as realpathSync7, renameSync as renameSync2, rmdirSync as rmdirSync2, writeFileSync as writeFileSync5, unlinkSync as unlinkSync3, chmodSync, openSync as openSync9, closeSync as closeSync9, fsyncSync as fsyncSync3, fstatSync as fstatSync9, readSync as readSync7, constants as constants8, linkSync as linkSync2 } from "fs";
14216
+ import { dirname as dirname12, join as join18, resolve as resolve11, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
14215
14217
  import { homedir as homedir2 } from "os";
14216
14218
 
14217
14219
  // src/lib/managed-policy.ts
@@ -14713,15 +14715,66 @@ var SUMI_SKILL_PERMISSIONS = [
14713
14715
  { action: "skill", resource: "skills-cli", effect: "allow" }
14714
14716
  ];
14715
14717
 
14718
+ // src/lib/codex-hook-trust-files.ts
14719
+ import { closeSync as closeSync8, constants as constants7, fstatSync as fstatSync8, fsyncSync as fsyncSync2, lstatSync as lstatSync16, openSync as openSync8, readSync as readSync6, writeFileSync as writeFileSync4 } from "fs";
14720
+ import { createHash as createHash12 } from "crypto";
14721
+ import { dirname as dirname11 } from "path";
14722
+ function need5(value, code) {
14723
+ if (!value)
14724
+ throw new Error(`CODEX_HOOK_TRUST_${code}`);
14725
+ }
14726
+ var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
14727
+ function safeParents(file) {
14728
+ for (let p = dirname11(file);; p = dirname11(p)) {
14729
+ const s = lstatSync16(p);
14730
+ need5(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
14731
+ need5((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
14732
+ if (dirname11(p) === p)
14733
+ break;
14734
+ }
14735
+ }
14736
+ function snapshot(file, privateFile = false, options = {}) {
14737
+ const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
14738
+ safeParents(file);
14739
+ const initial = lstatSync16(file, { bigint: true });
14740
+ need5(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
14741
+ need5(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
14742
+ const fd = openSync8(file, constants7.O_RDONLY | constants7.O_NOFOLLOW | constants7.O_NONBLOCK);
14743
+ try {
14744
+ const before = fstatSync8(fd, { bigint: true });
14745
+ need5(sameStat(initial, before), "INPUT_CHANGED");
14746
+ const bytes = Buffer.alloc(Number(before.size) + 1);
14747
+ let length = 0;
14748
+ while (length < bytes.length) {
14749
+ const n2 = readSync6(fd, bytes, length, bytes.length - length, null);
14750
+ if (!n2)
14751
+ break;
14752
+ length += n2;
14753
+ }
14754
+ need5(length === Number(before.size) && sameStat(before, fstatSync8(fd, { bigint: true })) && sameStat(before, lstatSync16(file, { bigint: true })), "INPUT_CHANGED");
14755
+ const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
14756
+ return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash12("sha256").update(content).digest("hex") };
14757
+ } finally {
14758
+ closeSync8(fd);
14759
+ }
14760
+ }
14761
+ function unchanged2(before) {
14762
+ const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
14763
+ need5(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
14764
+ }
14765
+
14716
14766
  // src/lib/agent-integration.ts
14717
- var sha3 = (data) => createHash12("sha256").update(data).digest("hex");
14767
+ var sha3 = (data) => createHash13("sha256").update(data).digest("hex");
14718
14768
  var ROOTS = NATIVE_SKILL_ROOTS;
14719
- function hasNoDarwinAcl(fd) {
14769
+ function hasTrustedDarwinAcl(fd, allowDenyOnly = false) {
14720
14770
  try {
14721
- const { dlopen, read: read2 } = __require("bun:ffi");
14771
+ const { dlopen, read: read2, ptr } = __require("bun:ffi");
14722
14772
  const library = dlopen("/usr/lib/libSystem.B.dylib", {
14723
14773
  acl_get_fd: { args: ["i32"], returns: "ptr" },
14724
14774
  acl_free: { args: ["ptr"], returns: "i32" },
14775
+ acl_valid: { args: ["ptr"], returns: "i32" },
14776
+ acl_get_entry: { args: ["ptr", "i32", "ptr"], returns: "i32" },
14777
+ acl_get_tag_type: { args: ["ptr", "ptr"], returns: "i32" },
14725
14778
  __error: { args: [], returns: "ptr" }
14726
14779
  });
14727
14780
  try {
@@ -14730,8 +14783,23 @@ function hasNoDarwinAcl(fd) {
14730
14783
  return false;
14731
14784
  const acl = library.symbols.acl_get_fd(fd);
14732
14785
  if (acl) {
14733
- library.symbols.acl_free(acl);
14734
- return false;
14786
+ try {
14787
+ if (!allowDenyOnly || library.symbols.acl_valid(acl) !== 0)
14788
+ return false;
14789
+ const entry = new BigUint64Array(1), tag = new Int32Array(1);
14790
+ for (let count = 0;count <= 128; count++) {
14791
+ const status = library.symbols.acl_get_entry(acl, count === 0 ? 0 : -1, ptr(entry));
14792
+ if (status !== 0) {
14793
+ return status === -1 && read2.i32(errno) === 22 && library.symbols.acl_valid(acl) === 0;
14794
+ }
14795
+ const value = read2.ptr(ptr(entry));
14796
+ if (count === 128 || !value || library.symbols.acl_get_tag_type(value, ptr(tag)) !== 0 || tag[0] !== 2)
14797
+ return false;
14798
+ }
14799
+ return false;
14800
+ } finally {
14801
+ library.symbols.acl_free(acl);
14802
+ }
14735
14803
  }
14736
14804
  return (acl === null || acl === 0) && read2.i32(errno) === 2;
14737
14805
  } finally {
@@ -14747,11 +14815,11 @@ function isSystemRootAlias(path) {
14747
14815
  const target = `/private${path}`, link = `private${path}`;
14748
14816
  const identity4 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}`;
14749
14817
  try {
14750
- const before = lstatSync16(path, { bigint: true });
14818
+ const before = lstatSync17(path, { bigint: true });
14751
14819
  if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync4(path) !== link)
14752
14820
  return false;
14753
14821
  const directories = ["/", "/private", target].map((directory) => {
14754
- const stat = lstatSync16(directory, { bigint: true });
14822
+ const stat = lstatSync17(directory, { bigint: true });
14755
14823
  if (!stat.isDirectory() || stat.uid !== 0n)
14756
14824
  throw new Error("Untrusted system alias directory");
14757
14825
  if (directory !== "/private/tmp") {
@@ -14759,23 +14827,23 @@ function isSystemRootAlias(path) {
14759
14827
  throw new Error("Writable system alias directory");
14760
14828
  } else if ((stat.mode & 0o1000n) === 0n)
14761
14829
  throw new Error("Unprotected system temporary directory");
14762
- const fd = openSync8(directory, constants7.O_RDONLY | constants7.O_DIRECTORY | constants7.O_NOFOLLOW);
14830
+ const fd = openSync9(directory, constants8.O_RDONLY | constants8.O_DIRECTORY | constants8.O_NOFOLLOW);
14763
14831
  try {
14764
- if (identity4(fstatSync8(fd, { bigint: true })) !== identity4(stat) || !hasNoDarwinAcl(fd) || identity4(fstatSync8(fd, { bigint: true })) !== identity4(stat))
14832
+ if (identity4(fstatSync9(fd, { bigint: true })) !== identity4(stat) || !hasTrustedDarwinAcl(fd) || identity4(fstatSync9(fd, { bigint: true })) !== identity4(stat))
14765
14833
  throw new Error("Unverified system alias directory ACL");
14766
14834
  } finally {
14767
- closeSync8(fd);
14835
+ closeSync9(fd);
14768
14836
  }
14769
14837
  return { directory, identity: identity4(stat) };
14770
14838
  });
14771
- return realpathSync7(path) === target && readlinkSync4(path) === link && identity4(lstatSync16(path, { bigint: true })) === identity4(before) && directories.every((item) => identity4(lstatSync16(item.directory, { bigint: true })) === item.identity);
14839
+ return realpathSync7(path) === target && readlinkSync4(path) === link && identity4(lstatSync17(path, { bigint: true })) === identity4(before) && directories.every((item) => identity4(lstatSync17(item.directory, { bigint: true })) === item.identity);
14772
14840
  } catch {
14773
14841
  return false;
14774
14842
  }
14775
14843
  }
14776
14844
  function canonicalSystemPath(path) {
14777
14845
  const absolute = resolve11(path), root = `/${absolute.split(sep5)[1]}`;
14778
- if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync16(root, { throwIfNoEntry: false })?.isSymbolicLink())
14846
+ if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync17(root, { throwIfNoEntry: false })?.isSymbolicLink())
14779
14847
  return absolute;
14780
14848
  if (!isSystemRootAlias(root))
14781
14849
  throw new Error(`Refusing symlink path: ${root}`);
@@ -14784,9 +14852,9 @@ function canonicalSystemPath(path) {
14784
14852
  function assertSafePath(path) {
14785
14853
  let cursor = resolve11(path);
14786
14854
  while (true) {
14787
- if (lstatSync16(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
14855
+ if (lstatSync17(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
14788
14856
  throw new Error(`Refusing symlink path: ${cursor}`);
14789
- const parent = dirname11(cursor);
14857
+ const parent = dirname12(cursor);
14790
14858
  if (parent === cursor)
14791
14859
  return;
14792
14860
  cursor = parent;
@@ -14795,12 +14863,12 @@ function assertSafePath(path) {
14795
14863
  function readRootAlias(home, agent) {
14796
14864
  assertSafePath(home);
14797
14865
  const realHome = realpathSync7(home), alias = join18(realHome, `.${agent}`);
14798
- const stat = lstatSync16(alias, { throwIfNoEntry: false, bigint: true });
14866
+ const stat = lstatSync17(alias, { throwIfNoEntry: false, bigint: true });
14799
14867
  if (!stat?.isSymbolicLink())
14800
14868
  throw new Error(`Agent root alias changed after planning: ${alias}`);
14801
- const link = readlinkSync4(alias), targetPath = resolve11(dirname11(alias), link);
14869
+ const link = readlinkSync4(alias), targetPath = resolve11(dirname12(alias), link);
14802
14870
  assertSafePath(targetPath);
14803
- const targetStat = lstatSync16(targetPath, { throwIfNoEntry: false });
14871
+ const targetStat = lstatSync17(targetPath, { throwIfNoEntry: false });
14804
14872
  if (!targetStat?.isDirectory())
14805
14873
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
14806
14874
  const target = realpathSync7(targetPath), inside = relative5(realHome, target);
@@ -14813,7 +14881,7 @@ function rootAliases(home, allowed = false) {
14813
14881
  return [];
14814
14882
  const result = [];
14815
14883
  for (const agent of ["claude", "codex"])
14816
- if (lstatSync16(join18(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
14884
+ if (lstatSync17(join18(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
14817
14885
  result.push(readRootAlias(home, agent));
14818
14886
  if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep5) || other.target.startsWith(item.target + sep5))))
14819
14887
  throw new Error("Agent root alias targets must not overlap");
@@ -14848,7 +14916,7 @@ function disabledCodexSkillPaths(config, aliases) {
14848
14916
  continue;
14849
14917
  settings.set(document2, [...settings.get(document2) ?? [], entry.enabled === false]);
14850
14918
  }
14851
- return [...settings].filter(([, values]) => values.length === 1 && values[0] === true).map(([document2]) => dirname11(document2));
14919
+ return [...settings].filter(([, values]) => values.length === 1 && values[0] === true).map(([document2]) => dirname12(document2));
14852
14920
  }
14853
14921
  function recheckRootAliases(aliases) {
14854
14922
  for (const binding of aliases) {
@@ -14861,35 +14929,35 @@ function recheckRootAliases(aliases) {
14861
14929
  }
14862
14930
  function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
14863
14931
  assertSafePath(path);
14864
- const before = lstatSync16(path);
14932
+ const before = lstatSync17(path);
14865
14933
  if (!before.isFile() || before.size > maximum)
14866
14934
  throw new Error("Unsupported or oversized native skill file");
14867
- const descriptor = openSync8(path, constants7.O_RDONLY | constants7.O_NOFOLLOW | constants7.O_NONBLOCK);
14935
+ const descriptor = openSync9(path, constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK);
14868
14936
  try {
14869
- const opened = fstatSync8(descriptor);
14937
+ const opened = fstatSync9(descriptor);
14870
14938
  if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size)
14871
14939
  throw new Error("Native skill file changed while reading");
14872
14940
  const bytes = Buffer.allocUnsafe(before.size + 1);
14873
14941
  let length = 0;
14874
14942
  while (length < bytes.length) {
14875
- const count = readSync6(descriptor, bytes, length, bytes.length - length, null);
14943
+ const count = readSync7(descriptor, bytes, length, bytes.length - length, null);
14876
14944
  if (!count)
14877
14945
  break;
14878
14946
  length += count;
14879
14947
  }
14880
- const after = fstatSync8(descriptor), current = lstatSync16(path);
14948
+ const after = fstatSync9(descriptor), current = lstatSync17(path);
14881
14949
  if (current.dev !== opened.dev || current.ino !== opened.ino || current.size !== after.size || current.mtimeMs !== after.mtimeMs || current.ctimeMs !== after.ctimeMs || length !== before.size || after.size !== before.size || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs)
14882
14950
  throw new Error("Native skill file changed while reading");
14883
14951
  return bytes.subarray(0, length);
14884
14952
  } finally {
14885
- closeSync8(descriptor);
14953
+ closeSync9(descriptor);
14886
14954
  }
14887
14955
  }
14888
14956
  function treeHash(root) {
14889
- const hash2 = createHash12("sha256");
14957
+ const hash2 = createHash13("sha256");
14890
14958
  let bytes = 0, files = 0;
14891
14959
  function visit2(path) {
14892
- const stat = lstatSync16(path);
14960
+ const stat = lstatSync17(path);
14893
14961
  if (stat.isSymbolicLink())
14894
14962
  throw new Error(`Refusing symlink in skill: ${path}`);
14895
14963
  const rel = relative5(root, path);
@@ -14921,7 +14989,7 @@ function selectNativeMigrationTargets(inventory, manifest) {
14921
14989
  if (!isAbsolute11(target.projectRoot) || resolve11(target.projectRoot) !== target.projectRoot)
14922
14990
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
14923
14991
  const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative5(projectRoot, resolve11(projectRoot, target.path));
14924
- if (!existsSync9(projectRoot) || !lstatSync16(projectRoot).isDirectory())
14992
+ if (!existsSync9(projectRoot) || !lstatSync17(projectRoot).isDirectory())
14925
14993
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
14926
14994
  assertSafePath(projectRoot);
14927
14995
  if (isAbsolute11(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep5}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
@@ -14950,7 +15018,7 @@ function projectAncestorDirectories(projects) {
14950
15018
  if (depth >= 100)
14951
15019
  throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
14952
15020
  directories.add(path);
14953
- const parent = dirname11(path);
15021
+ const parent = dirname12(path);
14954
15022
  if (parent === path)
14955
15023
  break;
14956
15024
  path = parent;
@@ -14996,12 +15064,12 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14996
15064
  let reviewedCacheAliasFound = false;
14997
15065
  function verifiedSiblingCacheAlias(agent, path, parent) {
14998
15066
  const link = readlinkSync4(path), lexicalTarget = resolve11(parent, link);
14999
- if (isAbsolute11(link) ? link !== lexicalTarget : dirname11(link) !== ".")
15067
+ if (isAbsolute11(link) ? link !== lexicalTarget : dirname12(link) !== ".")
15000
15068
  return false;
15001
15069
  const target = canonicalSystemPath(lexicalTarget);
15002
- if (dirname11(target) !== parent)
15070
+ if (dirname12(target) !== parent)
15003
15071
  return false;
15004
- if (!lstatSync16(target, { throwIfNoEntry: false })?.isDirectory())
15072
+ if (!lstatSync17(target, { throwIfNoEntry: false })?.isDirectory())
15005
15073
  return false;
15006
15074
  const scan = cacheScans.get(target);
15007
15075
  if (!scan?.complete || scan.entries > maxAliasProofEntries)
@@ -15026,7 +15094,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
15026
15094
  return scan;
15027
15095
  if (!existsSync9(path))
15028
15096
  return scan;
15029
- const stat = lstatSync16(path);
15097
+ const stat = lstatSync17(path);
15030
15098
  if (stat.isFile()) {
15031
15099
  if (agent === "sumi" && depth === 1 && path.endsWith(".md"))
15032
15100
  throw new Error("NATIVE_SKILL_DRIFT: flat Sumi skill documents require a dedicated migration adapter");
@@ -15075,7 +15143,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
15075
15143
  if (isVendor && !options.includeVendor)
15076
15144
  continue;
15077
15145
  const child = join18(path, name);
15078
- if (isVendor && lstatSync16(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
15146
+ if (isVendor && lstatSync17(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
15079
15147
  const target = statSync8(child, { throwIfNoEntry: false });
15080
15148
  if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child, path)) {
15081
15149
  scan.entries++;
@@ -15290,15 +15358,21 @@ function planAgentIntegration(options) {
15290
15358
  }
15291
15359
  return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
15292
15360
  });
15293
- const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(join18(home, ".codex", "config.toml"), aliases)) ?? "") : {};
15361
+ const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join18(home, ".codex", "config.toml"), aliases)) : null;
15362
+ const codexConfig = Bun.TOML.parse(codexConfigBefore ?? "");
15294
15363
  const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, disabledVendorPaths: disabledCodexSkillPaths(codexConfig, aliases) });
15364
+ const codexPathConfig = options.agents.includes("codex") ? disableCodexSkills(codexConfigBefore ?? "", nativeSkills, aliases, canonicalAgentPath(join18(home, ".codex/skills", CLI_BRIDGE_NAME), aliases)) : undefined;
15365
+ const plannedCodexRules = codexPathConfig === undefined ? undefined : Bun.TOML.parse(codexPathConfig).skills?.config ?? [];
15295
15366
  const observedNativeSources = [];
15296
15367
  const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join18(entry.path, "SKILL.md")), canonicalAgentPath(join18(home, ".codex/plugins/cache"), aliases), resolve11(options.projectDir ?? home), (path) => {
15297
15368
  const bytes = readNativeBytes(path, 1024 * 1024);
15298
15369
  observedNativeSources.push({ path, sha256: sha3(bytes) });
15299
15370
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
15300
- }, codexConfig.skills?.config ?? [], codexConfig.plugins) : { skills: policy.bridge?.codexPluginSkills ?? [], inactivePlugins: policy.bridge?.codexInactivePlugins ?? [], sourceInputs: policy.bridge?.discovery?.codex?.codexInstallationInputs?.plugins ?? [] };
15371
+ }, codexConfig.skills?.config ?? [], codexConfig.plugins, plannedCodexRules) : { skills: policy.bridge?.codexPluginSkills ?? [], inactivePlugins: policy.bridge?.codexInactivePlugins ?? [], sourceInputs: policy.bridge?.discovery?.codex?.codexInstallationInputs?.plugins ?? [] };
15301
15372
  const codexPluginSkills = codexPluginControls.skills;
15373
+ if (options.agents.includes("codex") && !options.codexNativeCatalog && codexPluginSkills.length && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join18(home, ".codex/plugins/cache"), aliases), codexPluginSkills, (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)))) {
15374
+ throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; provide a fresh native catalog and discovery review");
15375
+ }
15302
15376
  const codexInactivePlugins = codexPluginControls.inactivePlugins;
15303
15377
  const codexDiscovery = discoveries.find((binding) => binding.agent === "codex");
15304
15378
  const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot + sep5) || input.sourceRoot.startsWith(root + sep5)) && codexDiscovery.sources.some((source) => source.path === join18(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
@@ -15373,7 +15447,9 @@ function planAgentIntegration(options) {
15373
15447
  changes.push({ path, before, after });
15374
15448
  if (agent === "codex") {
15375
15449
  const configPath = canonicalAgentPath(join18(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
15376
- const next = disableReviewedCodexPluginNames(disableCodexSkills(previous ?? "", nativeSkills, aliases, bridgePath), codexPluginSkills);
15450
+ if (previous !== codexConfigBefore)
15451
+ throw new Error(`Configuration changed during planning: ${configPath}`);
15452
+ const next = disableReviewedCodexPluginNames(codexPathConfig, codexPluginSkills);
15377
15453
  if (next !== (previous ?? ""))
15378
15454
  changes.push({ path: configPath, before: previous, after: next });
15379
15455
  }
@@ -15409,6 +15485,171 @@ function planAgentIntegration(options) {
15409
15485
  recheckRootAliases(aliases);
15410
15486
  return { dataDir: dataDir2, profileId, changes, nativeSkills, ...observedNativeSources.length ? { observedNativeSources } : {}, ...nextPolicy.bridge.codexPluginSkillReview ? { codexPluginSkillReview: nextPolicy.bridge.codexPluginSkillReview } : {}, observedPolicy: { path: policyPath, before: previousPolicy }, discoveryBefore: discoveries, discoveryAfter, ...aliases.length ? { rootAliases: aliases } : {}, ...retainedAgents.length ? { retainedReviewChecks: { home, projectDir: options.projectDir ?? home, agents: retainedAgents } } : {} };
15411
15487
  }
15488
+ function hookProjectionRefusal(reason) {
15489
+ throw new Error(`CLAUDE_MANAGED_HOOK_${reason}`);
15490
+ }
15491
+ function projectedHookAcl(path, observed) {
15492
+ if (process.platform !== "darwin")
15493
+ return;
15494
+ const identity4 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}:${stat.nlink}:${stat.size}:${stat.mtimeNs}`;
15495
+ const fd = openSync9(path, constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK | (observed.isDirectory() ? constants8.O_DIRECTORY : 0));
15496
+ try {
15497
+ if (identity4(fstatSync9(fd, { bigint: true })) !== identity4(observed) || !hasTrustedDarwinAcl(fd, true) || identity4(fstatSync9(fd, { bigint: true })) !== identity4(observed) || identity4(lstatSync17(path, { bigint: true })) !== identity4(observed))
15498
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15499
+ } finally {
15500
+ closeSync9(fd);
15501
+ }
15502
+ }
15503
+ function projectedHookExecutable(command, expectedResolved) {
15504
+ try {
15505
+ if (!isAbsolute11(command) || /[\0\r\n]/.test(command) || resolve11(command) !== command)
15506
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15507
+ command = canonicalSystemPath(command);
15508
+ const parents2 = (file = command) => {
15509
+ const result = [];
15510
+ for (let path = dirname12(file);; path = dirname12(path)) {
15511
+ const stat = lstatSync17(path, { bigint: true });
15512
+ if (!stat.isDirectory() || (stat.mode & 0o022n) !== 0n && !(stat.uid === 0n && (stat.mode & 0o1000n) !== 0n))
15513
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15514
+ projectedHookAcl(path, stat);
15515
+ result.push(`${path}:${stat.dev}:${stat.ino}:${stat.uid}:${stat.mode}`);
15516
+ if (dirname12(path) === path)
15517
+ break;
15518
+ }
15519
+ return result;
15520
+ };
15521
+ const parentIdentity = parents2(), link = lstatSync17(command, { bigint: true });
15522
+ if (link.uid !== BigInt(process.getuid()) || !link.isFile() && !link.isSymbolicLink())
15523
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15524
+ const linkIdentity = (stat) => `${stat.dev}:${stat.ino}:${stat.mode}:${stat.uid}:${stat.ctimeNs}`;
15525
+ const resolved = realpathSync7(command);
15526
+ if (expectedResolved !== undefined && resolved !== expectedResolved)
15527
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15528
+ const resolvedParentIdentity = parents2(resolved);
15529
+ const manifest = snapshot(join18(dirname12(dirname12(resolved)), "package.json"), false, { readOnlyPackage: true });
15530
+ projectedHookAcl(manifest.file, manifest.stat);
15531
+ const pkg = JSON.parse(manifest.text);
15532
+ if (pkg.name !== "@hasna/skills" || !/^\d+\.\d+\.\d+$/.test(pkg.version) || pkg.bin?.skills !== "bin/index.js" || resolved !== join18(dirname12(manifest.file), "bin/index.js"))
15533
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15534
+ const cli = snapshot(resolved, false, { readOnlyPackage: true });
15535
+ projectedHookAcl(cli.file, cli.stat);
15536
+ if ((cli.stat.mode & 0o100n) === 0n)
15537
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15538
+ const recheck = () => {
15539
+ try {
15540
+ if (realpathSync7(command) !== resolved || linkIdentity(lstatSync17(command, { bigint: true })) !== linkIdentity(link) || !isDeepStrictEqual4(parents2(), parentIdentity) || !isDeepStrictEqual4(parents2(resolved), resolvedParentIdentity))
15541
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15542
+ projectedHookAcl(cli.file, cli.stat);
15543
+ projectedHookAcl(manifest.file, manifest.stat);
15544
+ unchanged2(cli);
15545
+ unchanged2(manifest);
15546
+ } catch {
15547
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15548
+ }
15549
+ };
15550
+ recheck();
15551
+ return { resolved, sha256: cli.sha256, manifestSha256: manifest.sha256, recheck };
15552
+ } catch {
15553
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15554
+ }
15555
+ }
15556
+ function planClaudeManagedHookProjection(options) {
15557
+ if (typeof options.targetSettings !== "string" || Buffer.byteLength(options.targetSettings) > 1024 * 1024)
15558
+ hookProjectionRefusal("TARGET_INVALID");
15559
+ const targetSha256 = sha3(options.targetSettings);
15560
+ if (!/^[a-f0-9]{64}$/.test(options.expectedTargetSha256) || options.expectedTargetSha256 !== targetSha256)
15561
+ hookProjectionRefusal("TARGET_CHANGED");
15562
+ let target;
15563
+ try {
15564
+ target = jsonObject(options.targetSettings, "target");
15565
+ } catch {
15566
+ hookProjectionRefusal("TARGET_INVALID");
15567
+ }
15568
+ const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15569
+ const policyPath = join18(dataDir2, "agent-policy.json");
15570
+ let policyBytes, settingsBytes, settingsPath, command, profile;
15571
+ try {
15572
+ policyBytes = readNativeBytes(policyPath, 1024 * 1024);
15573
+ const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
15574
+ if (!snapshot2 || snapshot2.text !== policyBytes.toString("utf8"))
15575
+ hookProjectionRefusal("SOURCE_UNVERIFIED");
15576
+ const binding = snapshot2.value.bridge;
15577
+ settingsPath = canonicalAgentPath(join18(home, AGENT_ADAPTERS.claude.config), binding?.rootAliases ?? []);
15578
+ settingsBytes = readNativeBytes(settingsPath, 1024 * 1024);
15579
+ command = binding?.commands?.claude;
15580
+ profile = binding?.profiles?.claude;
15581
+ if (typeof command !== "string" || typeof profile !== "string")
15582
+ hookProjectionRefusal("SOURCE_UNVERIFIED");
15583
+ assertManagedAgentBridge("claude", { home, dataDir: dataDir2, projectDir: options.projectDir, profileId: profile });
15584
+ } catch {
15585
+ hookProjectionRefusal("SOURCE_UNVERIFIED");
15586
+ }
15587
+ const executable = projectedHookExecutable(command);
15588
+ const sourceSha256 = sha3(JSON.stringify({
15589
+ home,
15590
+ policyPath,
15591
+ policy: sha3(policyBytes),
15592
+ settingsPath,
15593
+ settings: sha3(settingsBytes),
15594
+ executable: { path: executable.resolved, sha256: executable.sha256, manifestSha256: executable.manifestSha256 }
15595
+ }));
15596
+ if (options.expectedSourceSha256 !== undefined && options.expectedSourceSha256 !== sourceSha256)
15597
+ hookProjectionRefusal("SOURCE_CHANGED");
15598
+ const replacements = [];
15599
+ const observed = [executable];
15600
+ if (target.hooks !== undefined) {
15601
+ if (!target.hooks || typeof target.hooks !== "object" || Array.isArray(target.hooks))
15602
+ hookProjectionRefusal("TARGET_INVALID");
15603
+ for (const event of [...AGENT_ADAPTERS.claude.events, "PreToolUse"]) {
15604
+ const groups = target.hooks[event];
15605
+ if (groups === undefined)
15606
+ continue;
15607
+ if (!Array.isArray(groups))
15608
+ hookProjectionRefusal("TARGET_INVALID");
15609
+ let recognized = 0;
15610
+ for (const [groupIndex, group] of groups.entries()) {
15611
+ if (!group || !Array.isArray(group.hooks))
15612
+ hookProjectionRefusal("TARGET_INVALID");
15613
+ for (const [hookIndex, hook] of group.hooks.entries()) {
15614
+ if (hook?.type !== "command" || typeof hook.command !== "string")
15615
+ continue;
15616
+ if (!hook.command.includes("hook user-prompt --agent claude"))
15617
+ continue;
15618
+ const match = /^('(?:[^']|'\\'')*') hook user-prompt --agent claude --selection-profile ([A-Za-z0-9][A-Za-z0-9._-]{0,127}) --event ([A-Za-z]+)$/.exec(hook.command);
15619
+ if (!match || match[3] !== event)
15620
+ hookProjectionRefusal("UNRECOGNIZED");
15621
+ const oldCommand = match[1].slice(1, -1).replaceAll("'\\''", "'");
15622
+ let rendered;
15623
+ try {
15624
+ rendered = renderAgentHookCommand(oldCommand, "claude", match[2], event);
15625
+ } catch {
15626
+ hookProjectionRefusal("UNRECOGNIZED");
15627
+ }
15628
+ if (rendered !== hook.command)
15629
+ hookProjectionRefusal("UNRECOGNIZED");
15630
+ const legacy = projectedHookExecutable(oldCommand, executable.resolved);
15631
+ if (legacy.resolved !== executable.resolved || legacy.sha256 !== executable.sha256 || legacy.manifestSha256 !== executable.manifestSha256)
15632
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15633
+ observed.push(legacy);
15634
+ if (++recognized > 1)
15635
+ hookProjectionRefusal("AMBIGUOUS");
15636
+ const replacement = renderAgentHookCommand(command, "claude", profile, event);
15637
+ if (replacement !== hook.command)
15638
+ replacements.push({ event, groupIndex, hookIndex, beforeSha256: sha3(hook.command), command: replacement });
15639
+ }
15640
+ }
15641
+ }
15642
+ }
15643
+ try {
15644
+ if (!readNativeBytes(policyPath, 1024 * 1024).equals(policyBytes) || !readNativeBytes(settingsPath, 1024 * 1024).equals(settingsBytes))
15645
+ hookProjectionRefusal("SOURCE_CHANGED");
15646
+ } catch {
15647
+ hookProjectionRefusal("SOURCE_CHANGED");
15648
+ }
15649
+ for (const item of observed)
15650
+ item.recheck();
15651
+ return { schema: "hasna.claude-managed-hook-projection.v1", targetSha256, sourceSha256, replacements };
15652
+ }
15412
15653
  function planClaudeStopHookUpdate(options) {
15413
15654
  return planClaudeHookEventsUpdate({ ...options, events: ["Stop"] });
15414
15655
  }
@@ -15441,10 +15682,10 @@ function planClaudeHookEventsUpdate(options) {
15441
15682
  const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15442
15683
  const policyPath = join18(dataDir2, "agent-policy.json");
15443
15684
  assertSafePath(policyPath);
15444
- const snapshot = readManagedSkillPolicySnapshot(dataDir2);
15445
- if (!snapshot)
15685
+ const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
15686
+ if (!snapshot2)
15446
15687
  return null;
15447
- const policy = snapshot.value, bridge = policy.bridge;
15688
+ const policy = snapshot2.value, bridge = policy.bridge;
15448
15689
  if (!bridge || bridge.home !== home || bridge.version !== CLI_BRIDGE_VERSION || bridge.digest !== CLI_BRIDGE_DIGEST || !Array.isArray(bridge.agents) || !bridge.agents.length || bridge.agents.some((agent) => !INTEGRATION_AGENTS.includes(agent)) || new Set(bridge.agents).size !== bridge.agents.length || !bridge.discovery || typeof bridge.discovery !== "object" || Array.isArray(bridge.discovery)) {
15449
15690
  throw new Error("Invalid managed bridge for Claude settings update");
15450
15691
  }
@@ -15452,7 +15693,7 @@ function planClaudeHookEventsUpdate(options) {
15452
15693
  recheckRootAliases(aliases);
15453
15694
  const path = canonicalAgentPath(join18(home, ".claude", "settings.json"), aliases);
15454
15695
  assertSafePath(path);
15455
- const before = lstatSync16(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
15696
+ const before = lstatSync17(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
15456
15697
  if ((before === null ? "absent" : sha3(before)) !== options.expectedSettingsSha256)
15457
15698
  throw new Error("Claude settings preimage changed");
15458
15699
  assertClaudeHookEventsReplacement(before, options.replacement, options.events);
@@ -15482,7 +15723,7 @@ function planClaudeHookEventsUpdate(options) {
15482
15723
  const nextPolicy = { ...policy, bridge: { ...bridge, discovery: Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) } };
15483
15724
  const changes = before === options.replacement ? [] : [{ path, before, after: options.replacement }];
15484
15725
  if (JSON.stringify(nextPolicy) !== JSON.stringify(policy))
15485
- changes.push({ path: policyPath, before: snapshot.text, after: serializeManagedSkillPolicy(nextPolicy) });
15726
+ changes.push({ path: policyPath, before: snapshot2.text, after: serializeManagedSkillPolicy(nextPolicy) });
15486
15727
  recheckRootAliases(aliases);
15487
15728
  const nativeSkills = inventoryNativeSkills(home, {
15488
15729
  agents: consumers,
@@ -15497,7 +15738,7 @@ function planClaudeHookEventsUpdate(options) {
15497
15738
  profileId: policy.profileId ?? "default",
15498
15739
  changes,
15499
15740
  nativeSkills,
15500
- observedPolicy: { path: policyPath, before: snapshot.text },
15741
+ observedPolicy: { path: policyPath, before: snapshot2.text },
15501
15742
  discoveryBefore: discoveries,
15502
15743
  discoveryAfter,
15503
15744
  rootAliases: aliases,
@@ -15506,10 +15747,10 @@ function planClaudeHookEventsUpdate(options) {
15506
15747
  }
15507
15748
  function atomicWrite2(path, content) {
15508
15749
  assertSafePath(path);
15509
- mkdirSync5(dirname11(path), { recursive: true, mode: 448 });
15750
+ mkdirSync5(dirname12(path), { recursive: true, mode: 448 });
15510
15751
  const temporary = `${path}.skills-${randomUUID2()}`;
15511
15752
  try {
15512
- writeFileSync4(temporary, content, { mode: 384, flag: "wx" });
15753
+ writeFileSync5(temporary, content, { mode: 384, flag: "wx" });
15513
15754
  renameSync2(temporary, path);
15514
15755
  } finally {
15515
15756
  if (existsSync9(temporary))
@@ -15520,10 +15761,10 @@ function planAgentSettingsWitnessUpgrade(options) {
15520
15761
  if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3].includes(options.targetCodexVersion)))
15521
15762
  throw new Error("Invalid Codex witness target version");
15522
15763
  const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15523
- const snapshot = readManagedSkillPolicySnapshot(dataDir2);
15524
- if (!snapshot || sha3(snapshot.text) !== options.expectedPolicySha256)
15764
+ const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
15765
+ if (!snapshot2 || sha3(snapshot2.text) !== options.expectedPolicySha256)
15525
15766
  throw new Error("Managed policy preimage changed");
15526
- const policy = snapshot.value, aliases = policy.bridge?.rootAliases ?? [];
15767
+ const policy = snapshot2.value, aliases = policy.bridge?.rootAliases ?? [];
15527
15768
  recheckRootAliases(aliases);
15528
15769
  const binding = policy.bridge?.discovery?.[options.agent];
15529
15770
  if (!binding || binding.agent !== options.agent || binding.method !== "reviewed")
@@ -15567,7 +15808,7 @@ function planAgentSettingsWitnessUpgrade(options) {
15567
15808
  const replacement = { ...binding, sources: rebound };
15568
15809
  verifyAgentDiscovery(replacement);
15569
15810
  const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
15570
- return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join18(dataDir2, "agent-policy.json"), before: snapshot.text, after }], nativeSkills: [], observedPolicy: { path: join18(dataDir2, "agent-policy.json"), before: snapshot.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], 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 })) } };
15811
+ return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join18(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join18(dataDir2, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], 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 })) } };
15571
15812
  }
15572
15813
  function applyAgentIntegration(plan) {
15573
15814
  for (const source of plan.observedNativeSources ?? [])
@@ -15620,7 +15861,7 @@ function applyAgentIntegration(plan) {
15620
15861
  for (const [index, change] of plan.changes.entries())
15621
15862
  if (change.before !== null) {
15622
15863
  const backup = join18(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
15623
- writeFileSync4(backup, change.before, { mode: 384, flag: "wx" });
15864
+ writeFileSync5(backup, change.before, { mode: 384, flag: "wx" });
15624
15865
  if (readFileSync12(backup, "utf8") !== change.before)
15625
15866
  throw new Error("Native configuration preservation readback failed");
15626
15867
  backups.push(backup);
@@ -15630,7 +15871,7 @@ function applyAgentIntegration(plan) {
15630
15871
  recheckRootAliases(aliases);
15631
15872
  if (currentText(change.path) !== change.before)
15632
15873
  throw new Error(`Configuration changed after planning: ${change.path}`);
15633
- for (let directory = dirname11(change.path);!existsSync9(directory); directory = dirname11(directory))
15874
+ for (let directory = dirname12(change.path);!existsSync9(directory); directory = dirname12(directory))
15634
15875
  createdDirectories.add(directory);
15635
15876
  const after = change.after;
15636
15877
  atomicWrite2(change.path, after);
@@ -15681,11 +15922,11 @@ function applyAgentIntegration(plan) {
15681
15922
  }
15682
15923
  function syncArchiveDirectory(path) {
15683
15924
  assertSafePath(path);
15684
- const descriptor = openSync8(path, constants7.O_RDONLY | constants7.O_DIRECTORY | constants7.O_NOFOLLOW | constants7.O_NONBLOCK);
15925
+ const descriptor = openSync9(path, constants8.O_RDONLY | constants8.O_DIRECTORY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK);
15685
15926
  try {
15686
- fsyncSync2(descriptor);
15927
+ fsyncSync3(descriptor);
15687
15928
  } finally {
15688
- closeSync8(descriptor);
15929
+ closeSync9(descriptor);
15689
15930
  }
15690
15931
  }
15691
15932
  function writeArchiveJournal(path, value) {
@@ -15693,18 +15934,18 @@ function writeArchiveJournal(path, value) {
15693
15934
  const temporary = `${path}.skills-${randomUUID2()}`;
15694
15935
  let descriptor;
15695
15936
  try {
15696
- descriptor = openSync8(temporary, constants7.O_WRONLY | constants7.O_CREAT | constants7.O_EXCL | constants7.O_NOFOLLOW, 384);
15697
- writeFileSync4(descriptor, `${JSON.stringify(value)}
15937
+ descriptor = openSync9(temporary, constants8.O_WRONLY | constants8.O_CREAT | constants8.O_EXCL | constants8.O_NOFOLLOW, 384);
15938
+ writeFileSync5(descriptor, `${JSON.stringify(value)}
15698
15939
  `);
15699
- fsyncSync2(descriptor);
15700
- closeSync8(descriptor);
15940
+ fsyncSync3(descriptor);
15941
+ closeSync9(descriptor);
15701
15942
  descriptor = undefined;
15702
15943
  renameSync2(temporary, path);
15703
- syncArchiveDirectory(dirname11(path));
15944
+ syncArchiveDirectory(dirname12(path));
15704
15945
  } finally {
15705
15946
  if (descriptor !== undefined)
15706
- closeSync8(descriptor);
15707
- if (lstatSync16(temporary, { throwIfNoEntry: false }))
15947
+ closeSync9(descriptor);
15948
+ if (lstatSync17(temporary, { throwIfNoEntry: false }))
15708
15949
  unlinkSync3(temporary);
15709
15950
  }
15710
15951
  }
@@ -15738,7 +15979,7 @@ function archiveNativeSkills(inventory, options) {
15738
15979
  const moved = [];
15739
15980
  const verifyArchive = (entry) => {
15740
15981
  assertSafePath(entry.archive);
15741
- const stat = lstatSync16(entry.archive);
15982
+ const stat = lstatSync17(entry.archive);
15742
15983
  if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
15743
15984
  throw new Error("Archive type changed");
15744
15985
  if ((entry.discoveryOnly ? sha3(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
@@ -15746,14 +15987,14 @@ function archiveNativeSkills(inventory, options) {
15746
15987
  };
15747
15988
  assertSafePath(archiveRoot);
15748
15989
  const created = [];
15749
- for (let path = archiveRoot;!existsSync9(path); path = dirname11(path))
15990
+ for (let path = archiveRoot;!existsSync9(path); path = dirname12(path))
15750
15991
  created.push(path);
15751
15992
  mkdirSync5(archiveRoot, { recursive: true, mode: 448 });
15752
15993
  try {
15753
15994
  for (const directory of created)
15754
15995
  syncArchiveDirectory(directory);
15755
15996
  if (created.length)
15756
- syncArchiveDirectory(dirname11(created.at(-1)));
15997
+ syncArchiveDirectory(dirname12(created.at(-1)));
15757
15998
  writeArchiveJournal(receiptPath, journal);
15758
15999
  for (const [index, entry] of selected.entries()) {
15759
16000
  recheckRootAliases(aliases);
@@ -15765,7 +16006,7 @@ function archiveNativeSkills(inventory, options) {
15765
16006
  writeArchiveJournal(receiptPath, journal);
15766
16007
  renameSync2(move.source, move.archive);
15767
16008
  moved.push(move);
15768
- syncArchiveDirectory(dirname11(move.source));
16009
+ syncArchiveDirectory(dirname12(move.source));
15769
16010
  syncArchiveDirectory(archiveRoot);
15770
16011
  verifyArchive(move);
15771
16012
  move.status = "archived";
@@ -15786,7 +16027,7 @@ function archiveNativeSkills(inventory, options) {
15786
16027
  recheckRootAliases(aliases);
15787
16028
  assertSafePath(entry.source);
15788
16029
  assertSafePath(entry.archive);
15789
- if (lstatSync16(entry.source, { throwIfNoEntry: false })) {
16030
+ if (lstatSync17(entry.source, { throwIfNoEntry: false })) {
15790
16031
  entry.status = "recovery-required";
15791
16032
  entry.conflict = "source-occupied";
15792
16033
  continue;
@@ -15800,10 +16041,10 @@ function archiveNativeSkills(inventory, options) {
15800
16041
  }
15801
16042
  if (entry.discoveryOnly) {
15802
16043
  linkSync2(entry.archive, entry.source);
15803
- syncArchiveDirectory(dirname11(entry.source));
16044
+ syncArchiveDirectory(dirname12(entry.source));
15804
16045
  unlinkSync3(entry.archive);
15805
16046
  } else {
15806
- if (lstatSync16(entry.source, { throwIfNoEntry: false })) {
16047
+ if (lstatSync17(entry.source, { throwIfNoEntry: false })) {
15807
16048
  entry.status = "recovery-required";
15808
16049
  entry.conflict = "source-occupied";
15809
16050
  continue;
@@ -15811,7 +16052,7 @@ function archiveNativeSkills(inventory, options) {
15811
16052
  renameSync2(entry.archive, entry.source);
15812
16053
  }
15813
16054
  entry.status = "restored";
15814
- syncArchiveDirectory(dirname11(entry.source));
16055
+ syncArchiveDirectory(dirname12(entry.source));
15815
16056
  syncArchiveDirectory(archiveRoot);
15816
16057
  } catch {
15817
16058
  entry.status = "recovery-required";
@@ -15836,10 +16077,10 @@ function assertManagedAgentBridge(agent, options = {}) {
15836
16077
  function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
15837
16078
  const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15838
16079
  assertSafePath(join18(dataDir2, "agent-policy.json"));
15839
- const snapshot = readManagedSkillPolicySnapshot(dataDir2);
15840
- if (!snapshot)
16080
+ const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
16081
+ if (!snapshot2)
15841
16082
  throw new Error("NATIVE_SKILL_DRIFT: install the Skills bridge with skills hook install");
15842
- const binding = snapshot.value.bridge;
16083
+ const binding = snapshot2.value.bridge;
15843
16084
  if (!binding || binding.version !== CLI_BRIDGE_VERSION || binding.digest !== CLI_BRIDGE_DIGEST || binding.home !== home || !Array.isArray(binding.agents) || !binding.agents.includes(agent))
15844
16085
  throw new Error("NATIVE_SKILL_DRIFT: the managed Skills bridge binding is missing or incompatible; run skills hook install");
15845
16086
  const aliases = binding.rootAliases ?? [];
@@ -15980,7 +16221,7 @@ function isSkillsOwnershipMarker(marker) {
15980
16221
  function hasSkillsOwnershipMarker(dir) {
15981
16222
  const path = join19(dir, SYNC_MARKER_FILE);
15982
16223
  try {
15983
- if (!lstatSync17(path).isFile())
16224
+ if (!lstatSync18(path).isFile())
15984
16225
  return false;
15985
16226
  const marker = JSON.parse(readFileSync13(path, "utf8"));
15986
16227
  return isSkillsOwnershipMarker(marker);
@@ -16206,7 +16447,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
16206
16447
  const action = dirExists ? "update" : "create";
16207
16448
  if (options.dryRun)
16208
16449
  return { action, path: skillMdPath };
16209
- const parentDir = dirname12(dir);
16450
+ const parentDir = dirname13(dir);
16210
16451
  mkdirSync6(parentDir, { recursive: true });
16211
16452
  const transactionDir = mkdtempSync(join19(parentDir, `.hasna-skills-write-${basename6(dir)}-`));
16212
16453
  const candidateDir = join19(transactionDir, "candidate");
@@ -16228,10 +16469,10 @@ function writeManagedSkillDir(dir, skillMd, options) {
16228
16469
  } else {
16229
16470
  mkdirSync6(candidateDir, { recursive: true });
16230
16471
  }
16231
- writeFileSync5(candidateSkillMdPath, skillMd.endsWith(`
16472
+ writeFileSync6(candidateSkillMdPath, skillMd.endsWith(`
16232
16473
  `) ? skillMd : `${skillMd}
16233
16474
  `);
16234
- writeFileSync5(candidateMarkerPath, `${JSON.stringify(marker, null, 2)}
16475
+ writeFileSync6(candidateMarkerPath, `${JSON.stringify(marker, null, 2)}
16235
16476
  `);
16236
16477
  if (dirExists) {
16237
16478
  originalMoved = true;
@@ -16311,7 +16552,7 @@ function resolveSkillAlias(name) {
16311
16552
  }
16312
16553
 
16313
16554
  // src/lib/project-state.ts
16314
- import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
16555
+ import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
16315
16556
  import { join as join20 } from "path";
16316
16557
  var VALID_PIN_SOURCES = [
16317
16558
  "official",
@@ -16360,7 +16601,7 @@ function saveProjectConfig(config, targetDir = process.cwd()) {
16360
16601
  const dir = getProjectStateDir(targetDir);
16361
16602
  mkdirSync7(dir, { recursive: true });
16362
16603
  const normalized = normalizeProjectConfig({ ...config, updatedAt: new Date().toISOString() });
16363
- writeFileSync6(getProjectConfigPath(targetDir), JSON.stringify(normalized, null, 2) + `
16604
+ writeFileSync7(getProjectConfigPath(targetDir), JSON.stringify(normalized, null, 2) + `
16364
16605
  `);
16365
16606
  }
16366
16607
  function pinProjectSkill(name, details = {}, targetDir = process.cwd()) {
@@ -17179,7 +17420,7 @@ function exactProfileSelection(spec, profile) {
17179
17420
  }
17180
17421
 
17181
17422
  // src/lib/plugin-projection.ts
17182
- import { createHash as createHash13 } from "crypto";
17423
+ import { createHash as createHash14 } from "crypto";
17183
17424
  import { posix } from "path";
17184
17425
  var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
17185
17426
  var PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
@@ -17190,7 +17431,7 @@ function pluginNeed(value, message) {
17190
17431
  if (!value)
17191
17432
  pluginRefusal(message);
17192
17433
  }
17193
- var pluginHash = (bytes) => createHash13("sha256").update(bytes).digest("hex");
17434
+ var pluginHash = (bytes) => createHash14("sha256").update(bytes).digest("hex");
17194
17435
  function pluginObject(value) {
17195
17436
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
17196
17437
  }
@@ -17378,16 +17619,16 @@ function buildPluginProjection(entries) {
17378
17619
  }
17379
17620
 
17380
17621
  // src/lib/plugin-projection-store.ts
17381
- import { createHash as createHash14, randomUUID as randomUUID3 } from "crypto";
17382
- import { constants as constants8, closeSync as closeSync9, fchmodSync, fstatSync as fstatSync9, fsyncSync as fsyncSync3, linkSync as linkSync3, lstatSync as lstatSync18, mkdirSync as mkdirSync8, openSync as openSync9, opendirSync as opendirSync4, readSync as readSync7, renameSync as renameSync4, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync4, writeSync as writeSync2 } from "fs";
17383
- import { dirname as dirname13, isAbsolute as isAbsolute12, join as join25, parse as parse5, resolve as resolve12 } from "path";
17384
- var READ_FLAGS = constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK;
17385
- var WRITE_FLAGS = constants8.O_WRONLY | constants8.O_CREAT | constants8.O_EXCL | constants8.O_NOFOLLOW | constants8.O_NONBLOCK;
17622
+ import { createHash as createHash15, randomUUID as randomUUID3 } from "crypto";
17623
+ import { constants as constants9, closeSync as closeSync10, fchmodSync, fstatSync as fstatSync10, fsyncSync as fsyncSync4, linkSync as linkSync3, lstatSync as lstatSync19, mkdirSync as mkdirSync8, openSync as openSync10, opendirSync as opendirSync4, readSync as readSync8, renameSync as renameSync4, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync4, writeSync as writeSync2 } from "fs";
17624
+ import { dirname as dirname14, isAbsolute as isAbsolute12, join as join25, parse as parse5, resolve as resolve12 } from "path";
17625
+ var READ_FLAGS = constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK;
17626
+ var WRITE_FLAGS = constants9.O_WRONLY | constants9.O_CREAT | constants9.O_EXCL | constants9.O_NOFOLLOW | constants9.O_NONBLOCK;
17386
17627
  var uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined;
17387
17628
  var missing = (error) => Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
17388
17629
  function stat(path) {
17389
17630
  try {
17390
- return lstatSync18(path, { bigint: true });
17631
+ return lstatSync19(path, { bigint: true });
17391
17632
  } catch (error) {
17392
17633
  if (missing(error))
17393
17634
  return null;
@@ -17406,7 +17647,7 @@ function same3(left, right, identityOnly = false) {
17406
17647
  }
17407
17648
  function ancestors(path, create = false) {
17408
17649
  canonical5(path);
17409
- const result = [], parent = dirname13(path), filesystemRoot = parse5(parent).root;
17650
+ const result = [], parent = dirname14(path), filesystemRoot = parse5(parent).root;
17410
17651
  let current = filesystemRoot;
17411
17652
  for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
17412
17653
  if (segment)
@@ -17434,7 +17675,7 @@ function verifyAncestors(chain, identityOnly = true) {
17434
17675
  }
17435
17676
  function privateParent(path) {
17436
17677
  const chain = ancestors(path, true), parent = chain.at(-1);
17437
- pluginNeed(parent && parent.path === dirname13(path), "Plugin storage parent is unavailable");
17678
+ pluginNeed(parent && parent.path === dirname14(path), "Plugin storage parent is unavailable");
17438
17679
  owner(parent.stat);
17439
17680
  pluginNeed((parent.stat.mode & 0o7777n) === 0o700n, "Plugin storage parent must have mode 0700");
17440
17681
  return chain;
@@ -17463,19 +17704,19 @@ function readBytes(path, maximum, kind) {
17463
17704
  fileShape(before, maximum, kind);
17464
17705
  let fd;
17465
17706
  try {
17466
- fd = openSync9(path, READ_FLAGS);
17467
- const opened = fstatSync9(fd, { bigint: true });
17707
+ fd = openSync10(path, READ_FLAGS);
17708
+ const opened = fstatSync10(fd, { bigint: true });
17468
17709
  same3(before, opened);
17469
17710
  fileShape(opened, maximum, kind);
17470
17711
  const bytes = new Uint8Array(Number(opened.size));
17471
17712
  let offset = 0;
17472
17713
  while (offset < bytes.byteLength) {
17473
- const count = readSync7(fd, bytes, offset, bytes.byteLength - offset, null);
17714
+ const count = readSync8(fd, bytes, offset, bytes.byteLength - offset, null);
17474
17715
  pluginNeed(count > 0, "Plugin file changed while being read");
17475
17716
  offset += count;
17476
17717
  }
17477
- pluginNeed(readSync7(fd, new Uint8Array(1), 0, 1, null) === 0, "Plugin file grew while being read");
17478
- same3(opened, fstatSync9(fd, { bigint: true }));
17718
+ pluginNeed(readSync8(fd, new Uint8Array(1), 0, 1, null) === 0, "Plugin file grew while being read");
17719
+ same3(opened, fstatSync10(fd, { bigint: true }));
17479
17720
  same3(opened, stat(path));
17480
17721
  verifyAncestors(chain);
17481
17722
  return bytes;
@@ -17485,7 +17726,7 @@ function readBytes(path, maximum, kind) {
17485
17726
  return pluginRefusal("Cannot safely read plugin file");
17486
17727
  } finally {
17487
17728
  if (fd !== undefined)
17488
- closeSync9(fd);
17729
+ closeSync10(fd);
17489
17730
  }
17490
17731
  }
17491
17732
  function exactPaths(entries) {
@@ -17595,16 +17836,16 @@ function verifyPluginTree(root, witnesses) {
17595
17836
  function writeExclusive(path, bytes, mode) {
17596
17837
  let fd, created;
17597
17838
  try {
17598
- fd = openSync9(path, WRITE_FLAGS, mode);
17839
+ fd = openSync10(path, WRITE_FLAGS, mode);
17599
17840
  fchmodSync(fd, mode);
17600
- created = fstatSync9(fd, { bigint: true });
17841
+ created = fstatSync10(fd, { bigint: true });
17601
17842
  let offset = 0;
17602
17843
  while (offset < bytes.byteLength) {
17603
17844
  const count = writeSync2(fd, bytes, offset, bytes.byteLength - offset);
17604
17845
  pluginNeed(count > 0, "Plugin file write did not progress");
17605
17846
  offset += count;
17606
17847
  }
17607
- fsyncSync3(fd);
17848
+ fsyncSync4(fd);
17608
17849
  } catch (error) {
17609
17850
  if (created) {
17610
17851
  same3(created, stat(path), true);
@@ -17613,7 +17854,7 @@ function writeExclusive(path, bytes, mode) {
17613
17854
  throw error;
17614
17855
  } finally {
17615
17856
  if (fd !== undefined)
17616
- closeSync9(fd);
17857
+ closeSync10(fd);
17617
17858
  }
17618
17859
  }
17619
17860
  function materializePluginTree(root, entries) {
@@ -17716,20 +17957,20 @@ function pluginExecutableDigest(path) {
17716
17957
  fileShape(before, maximum, "executable");
17717
17958
  let fd;
17718
17959
  try {
17719
- fd = openSync9(path, READ_FLAGS);
17720
- const opened = fstatSync9(fd, { bigint: true });
17960
+ fd = openSync10(path, READ_FLAGS);
17961
+ const opened = fstatSync10(fd, { bigint: true });
17721
17962
  same3(before, opened);
17722
17963
  fileShape(opened, maximum, "executable");
17723
- const hash2 = createHash14("sha256"), buffer = new Uint8Array(64 * 1024);
17964
+ const hash2 = createHash15("sha256"), buffer = new Uint8Array(64 * 1024);
17724
17965
  let remaining = Number(opened.size);
17725
17966
  while (remaining > 0) {
17726
- const count = readSync7(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
17967
+ const count = readSync8(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
17727
17968
  pluginNeed(count > 0, "Plugin executable changed during hashing");
17728
17969
  hash2.update(buffer.subarray(0, count));
17729
17970
  remaining -= count;
17730
17971
  }
17731
- pluginNeed(readSync7(fd, buffer, 0, 1, null) === 0, "Plugin executable grew during hashing");
17732
- same3(opened, fstatSync9(fd, { bigint: true }));
17972
+ pluginNeed(readSync8(fd, buffer, 0, 1, null) === 0, "Plugin executable grew during hashing");
17973
+ same3(opened, fstatSync10(fd, { bigint: true }));
17733
17974
  same3(opened, stat(path));
17734
17975
  verifyAncestors(chain);
17735
17976
  return `sha256:${hash2.digest("hex")}`;
@@ -17739,7 +17980,7 @@ function pluginExecutableDigest(path) {
17739
17980
  return pluginRefusal("Cannot safely hash plugin executable");
17740
17981
  } finally {
17741
17982
  if (fd !== undefined)
17742
- closeSync9(fd);
17983
+ closeSync10(fd);
17743
17984
  }
17744
17985
  }
17745
17986
 
@@ -18149,29 +18390,29 @@ function validateManagedPluginWitnesses(value) {
18149
18390
  function safe3(path) {
18150
18391
  pluginText(path);
18151
18392
  pluginNeed(isAbsolute14(path) && resolve14(path) === path, "Invalid plugin registry path");
18152
- for (let at = path;; at = dirname14(at)) {
18153
- pluginNeed(!lstatSync19(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
18154
- if (at === dirname14(at))
18393
+ for (let at = path;; at = dirname15(at)) {
18394
+ pluginNeed(!lstatSync20(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
18395
+ if (at === dirname15(at))
18155
18396
  break;
18156
18397
  }
18157
18398
  }
18158
18399
  function readRegistry(path) {
18159
18400
  safe3(path);
18160
- const initial = lstatSync19(path);
18401
+ const initial = lstatSync20(path);
18161
18402
  pluginNeed(initial.isFile() && initial.size <= 1024 * 1024, "Plugin registry is not a bounded regular file");
18162
- const fd = openSync10(path, constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK);
18403
+ const fd = openSync11(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
18163
18404
  try {
18164
- const opened = fstatSync10(fd);
18405
+ const opened = fstatSync11(fd);
18165
18406
  pluginNeed(opened.isFile() && opened.ino === initial.ino && opened.dev === initial.dev, "Plugin registry changed during open");
18166
18407
  const bytes = Buffer.alloc(1024 * 1024 + 1);
18167
18408
  let length = 0;
18168
18409
  while (length < bytes.length) {
18169
- const count = readSync8(fd, bytes, length, bytes.length - length, null);
18410
+ const count = readSync9(fd, bytes, length, bytes.length - length, null);
18170
18411
  if (!count)
18171
18412
  break;
18172
18413
  length += count;
18173
18414
  }
18174
- const after = fstatSync10(fd), current = lstatSync19(path);
18415
+ const after = fstatSync11(fd), current = lstatSync20(path);
18175
18416
  safe3(path);
18176
18417
  pluginNeed(length <= 1024 * 1024 && length === opened.size && after.size === opened.size && after.mtimeMs === opened.mtimeMs && after.ctimeMs === opened.ctimeMs && current.dev === opened.dev && current.ino === opened.ino && current.mtimeMs === opened.mtimeMs && current.ctimeMs === opened.ctimeMs, "Plugin registry changed during read");
18177
18418
  let value;
@@ -18183,7 +18424,7 @@ function readRegistry(path) {
18183
18424
  pluginNeed(pluginObject(value) && value.version === 2 && pluginObject(value.plugins), "Unsupported native plugin registry schema");
18184
18425
  return { value, rawDigest: pluginHash(bytes.subarray(0, length)) };
18185
18426
  } finally {
18186
- closeSync10(fd);
18427
+ closeSync11(fd);
18187
18428
  }
18188
18429
  }
18189
18430
  function nativeVersionMatches(version, receipt) {
@@ -18192,7 +18433,7 @@ function nativeVersionMatches(version, receipt) {
18192
18433
  }
18193
18434
  function hashManagedPluginRegistry(path, managed) {
18194
18435
  validateManagedPluginWitnesses(managed);
18195
- const snapshot = readRegistry(path), plugins = snapshot.value.plugins;
18436
+ const snapshot2 = readRegistry(path), plugins = snapshot2.value.plugins;
18196
18437
  const seen = new Set, verified = new Set;
18197
18438
  let verifiedBytes = 0;
18198
18439
  for (const witness of managed) {
@@ -18201,7 +18442,7 @@ function hashManagedPluginRegistry(path, managed) {
18201
18442
  seen.add(target.pluginId);
18202
18443
  const rows = plugins[target.pluginId];
18203
18444
  pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
18204
- const [name, market] = target.pluginId.split("@"), cacheRoot = join27(dirname14(path), "cache", market, name);
18445
+ const [name, market] = target.pluginId.split("@"), cacheRoot = join27(dirname15(path), "cache", market, name);
18205
18446
  const scopes = new Set, approvals = new Map, currentFiles = new Map;
18206
18447
  plugins[target.pluginId] = rows.map((row) => {
18207
18448
  pluginNeed(pluginObject(row), "Invalid managed plugin registration");
@@ -18244,7 +18485,7 @@ function hashManagedPluginRegistry(path, managed) {
18244
18485
  return Object.fromEntries(Object.entries(row).filter(([key]) => !["version", "installPath", "sourceProducerPath", "previousProducerPaths", "lastUpdated"].includes(key)));
18245
18486
  });
18246
18487
  safe3(cacheRoot);
18247
- const before = lstatSync19(cacheRoot);
18488
+ const before = lstatSync20(cacheRoot);
18248
18489
  pluginNeed(before.isDirectory(), "Invalid managed plugin cache root");
18249
18490
  const directory = opendirSync5(cacheRoot), names = [];
18250
18491
  try {
@@ -18267,64 +18508,64 @@ function hashManagedPluginRegistry(path, managed) {
18267
18508
  }
18268
18509
  for (const row of rows)
18269
18510
  pluginNeed(names.includes(row.version), "Managed native plugin installation is missing");
18270
- const after = lstatSync19(cacheRoot);
18511
+ const after = lstatSync20(cacheRoot);
18271
18512
  safe3(cacheRoot);
18272
18513
  pluginNeed(before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs, "Managed cache version membership changed during verification");
18273
18514
  }
18274
- pluginNeed(readRegistry(path).rawDigest === snapshot.rawDigest, "Native plugin registry changed during content verification");
18275
- return pluginHash(JSON.stringify(snapshot.value));
18515
+ pluginNeed(readRegistry(path).rawDigest === snapshot2.rawDigest, "Native plugin registry changed during content verification");
18516
+ return pluginHash(JSON.stringify(snapshot2.value));
18276
18517
  }
18277
18518
  function captureManagedPluginRegistry(path, managedPlugins) {
18278
18519
  return { path, hashMode: "claude-plugin-registry", managedPlugins, sha256: hashManagedPluginRegistry(path, managedPlugins) };
18279
18520
  }
18280
18521
 
18281
18522
  // src/lib/claude-marketplace-registry.ts
18282
- import { createHash as createHash15 } from "crypto";
18283
- import { closeSync as closeSync11, constants as constants10, fstatSync as fstatSync11, lstatSync as lstatSync20, openSync as openSync11, readSync as readSync9 } from "fs";
18284
- import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute15, resolve as resolve15 } from "path";
18523
+ import { createHash as createHash16 } from "crypto";
18524
+ import { closeSync as closeSync12, constants as constants11, fstatSync as fstatSync12, lstatSync as lstatSync21, openSync as openSync12, readSync as readSync10 } from "fs";
18525
+ import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute15, resolve as resolve15 } from "path";
18285
18526
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
18286
- function need5(value, reason) {
18527
+ function need6(value, reason) {
18287
18528
  if (!value)
18288
18529
  throw new Error(`Claude marketplace registry ${reason}`);
18289
18530
  }
18290
18531
  function absolutePath3(value) {
18291
- need5(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute15(value) && resolve15(value) === value, "requires a normalized absolute path");
18532
+ need6(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute15(value) && resolve15(value) === value, "requires a normalized absolute path");
18292
18533
  }
18293
18534
  var sameFile2 = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
18294
18535
  function parents2(path) {
18295
18536
  const result = [];
18296
- for (let at = dirname15(path);; at = dirname15(at)) {
18297
- const stat2 = lstatSync20(at, { bigint: true });
18298
- need5(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
18537
+ for (let at = dirname16(path);; at = dirname16(at)) {
18538
+ const stat2 = lstatSync21(at, { bigint: true });
18539
+ need6(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
18299
18540
  result.push([at, stat2]);
18300
- if (at === dirname15(at))
18541
+ if (at === dirname16(at))
18301
18542
  return result;
18302
18543
  }
18303
18544
  }
18304
18545
  function readRegistry2(path, budget) {
18305
18546
  absolutePath3(path);
18306
- need5(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
18307
- need5(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
18308
- const ancestors2 = parents2(path), initial = lstatSync20(path, { bigint: true });
18309
- need5(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
18310
- need5(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
18311
- const fd = openSync11(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
18547
+ need6(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
18548
+ need6(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
18549
+ const ancestors2 = parents2(path), initial = lstatSync21(path, { bigint: true });
18550
+ need6(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
18551
+ need6(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
18552
+ const fd = openSync12(path, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
18312
18553
  try {
18313
- const opened = fstatSync11(fd, { bigint: true });
18314
- need5(opened.isFile() && sameFile2(initial, opened), "changed during open");
18554
+ const opened = fstatSync12(fd, { bigint: true });
18555
+ need6(opened.isFile() && sameFile2(initial, opened), "changed during open");
18315
18556
  const bytes = Buffer.alloc(Number(opened.size) + 1);
18316
18557
  let length = 0;
18317
18558
  while (length < bytes.length) {
18318
- const count = readSync9(fd, bytes, length, bytes.length - length, null);
18559
+ const count = readSync10(fd, bytes, length, bytes.length - length, null);
18319
18560
  if (!count)
18320
18561
  break;
18321
18562
  length += count;
18322
18563
  }
18323
- const after = fstatSync11(fd, { bigint: true }), current = lstatSync20(path, { bigint: true });
18324
- need5(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
18564
+ const after = fstatSync12(fd, { bigint: true }), current = lstatSync21(path, { bigint: true });
18565
+ need6(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
18325
18566
  for (const [ancestor, before] of ancestors2) {
18326
- const now = lstatSync20(ancestor, { bigint: true });
18327
- need5(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
18567
+ const now = lstatSync21(ancestor, { bigint: true });
18568
+ need6(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
18328
18569
  }
18329
18570
  budget.remaining -= length;
18330
18571
  try {
@@ -18333,7 +18574,7 @@ function readRegistry2(path, budget) {
18333
18574
  throw new Error("Claude marketplace registry is not strict UTF-8");
18334
18575
  }
18335
18576
  } finally {
18336
- closeSync11(fd);
18577
+ closeSync12(fd);
18337
18578
  }
18338
18579
  }
18339
18580
  function parse6(text2) {
@@ -18344,7 +18585,7 @@ function parse6(text2) {
18344
18585
  at++;
18345
18586
  };
18346
18587
  function string() {
18347
- need5(text2[at] === '"', "contains invalid JSON");
18588
+ need6(text2[at] === '"', "contains invalid JSON");
18348
18589
  const start = at++;
18349
18590
  while (at < text2.length) {
18350
18591
  const character = text2[at++];
@@ -18355,7 +18596,7 @@ function parse6(text2) {
18355
18596
  } catch {
18356
18597
  throw new Error("Claude marketplace registry contains invalid JSON string");
18357
18598
  }
18358
- need5(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
18599
+ need6(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
18359
18600
  return value2;
18360
18601
  }
18361
18602
  if (character === "\\")
@@ -18364,7 +18605,7 @@ function parse6(text2) {
18364
18605
  throw new Error("Claude marketplace registry contains an unterminated JSON string");
18365
18606
  }
18366
18607
  function value(depth) {
18367
- need5(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
18608
+ need6(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
18368
18609
  space();
18369
18610
  const character = text2[at];
18370
18611
  if (character === '"')
@@ -18378,17 +18619,17 @@ function parse6(text2) {
18378
18619
  if (object5) {
18379
18620
  space();
18380
18621
  const key = string();
18381
- need5(!keys.has(key), "contains a duplicate JSON key");
18622
+ need6(!keys.has(key), "contains a duplicate JSON key");
18382
18623
  keys.add(key);
18383
18624
  space();
18384
- need5(text2[at++] === ":", "contains invalid JSON");
18625
+ need6(text2[at++] === ":", "contains invalid JSON");
18385
18626
  entries.push([key, value(depth + 1)]);
18386
18627
  } else
18387
18628
  items.push(value(depth + 1));
18388
18629
  space();
18389
18630
  if (text2[at] === end)
18390
18631
  break;
18391
- need5(text2[at++] === ",", "contains invalid JSON");
18632
+ need6(text2[at++] === ",", "contains invalid JSON");
18392
18633
  }
18393
18634
  at++;
18394
18635
  return object5 ? { kind: "object", entries } : { kind: "array", items };
@@ -18400,14 +18641,14 @@ function parse6(text2) {
18400
18641
  }
18401
18642
  numberPattern.lastIndex = at;
18402
18643
  const number = numberPattern.exec(text2);
18403
- need5(number, "contains invalid JSON");
18644
+ need6(number, "contains invalid JSON");
18404
18645
  at += number[0].length;
18405
18646
  return { kind: "number", value: number[0] };
18406
18647
  }
18407
18648
  const root2 = value(0);
18408
18649
  space();
18409
- need5(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
18410
- need5(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
18650
+ need6(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
18651
+ need6(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
18411
18652
  return root2;
18412
18653
  }
18413
18654
  function exactKeys(value, keys) {
@@ -18415,7 +18656,7 @@ function exactKeys(value, keys) {
18415
18656
  }
18416
18657
  var field = (object5, key) => object5.entries.find(([name]) => name === key)?.[1];
18417
18658
  function textField(value) {
18418
- need5(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
18659
+ need6(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
18419
18660
  return value.value;
18420
18661
  }
18421
18662
  function normalizeRow(row, version = 1) {
@@ -18423,7 +18664,7 @@ function normalizeRow(row, version = 1) {
18423
18664
  if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
18424
18665
  return;
18425
18666
  const autoUpdate = field(row, "autoUpdate");
18426
- need5(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
18667
+ need6(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
18427
18668
  }
18428
18669
  const source = field(row, "source");
18429
18670
  if (source?.kind !== "object")
@@ -18439,77 +18680,29 @@ function normalizeRow(row, version = 1) {
18439
18680
  absolutePath3(sourceLocation);
18440
18681
  absolutePath3(textField(field(row, "installLocation")));
18441
18682
  const updated = textField(field(row, "lastUpdated"));
18442
- need5(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
18683
+ need6(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(updated), "contains an invalid recognized update timestamp");
18443
18684
  const millis = Date.parse(updated);
18444
- need5(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
18685
+ need6(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
18445
18686
  row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
18446
18687
  }
18447
18688
  function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
18448
18689
  const value = parse6(readRegistry2(path, budget));
18449
18690
  for (const [, row] of value.entries)
18450
18691
  normalizeRow(row);
18451
- const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
18692
+ const sha2562 = createHash16("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
18452
18693
  return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
18453
18694
  }
18454
18695
  function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
18455
18696
  const value = parse6(readRegistry2(path, budget));
18456
18697
  for (const [, row] of value.entries)
18457
18698
  normalizeRow(row, 2);
18458
- const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
18699
+ const sha2562 = createHash16("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
18459
18700
  return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
18460
18701
  }
18461
18702
 
18462
18703
  // src/lib/codex-hook-discovery-recovery.ts
18463
18704
  import { createHash as createHash17 } from "crypto";
18464
18705
 
18465
- // src/lib/codex-hook-trust-files.ts
18466
- import { closeSync as closeSync12, constants as constants11, fstatSync as fstatSync12, fsyncSync as fsyncSync4, lstatSync as lstatSync21, openSync as openSync12, readSync as readSync10, writeFileSync as writeFileSync7 } from "fs";
18467
- import { createHash as createHash16 } from "crypto";
18468
- import { dirname as dirname16 } from "path";
18469
- function need6(value, code) {
18470
- if (!value)
18471
- throw new Error(`CODEX_HOOK_TRUST_${code}`);
18472
- }
18473
- var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
18474
- function safeParents(file) {
18475
- for (let p = dirname16(file);; p = dirname16(p)) {
18476
- const s = lstatSync21(p);
18477
- need6(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
18478
- need6((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
18479
- if (dirname16(p) === p)
18480
- break;
18481
- }
18482
- }
18483
- function snapshot(file, privateFile = false, options = {}) {
18484
- const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
18485
- safeParents(file);
18486
- const initial = lstatSync21(file, { bigint: true });
18487
- need6(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
18488
- need6(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
18489
- const fd = openSync12(file, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
18490
- try {
18491
- const before = fstatSync12(fd, { bigint: true });
18492
- need6(sameStat(initial, before), "INPUT_CHANGED");
18493
- const bytes = Buffer.alloc(Number(before.size) + 1);
18494
- let length = 0;
18495
- while (length < bytes.length) {
18496
- const n2 = readSync10(fd, bytes, length, bytes.length - length, null);
18497
- if (!n2)
18498
- break;
18499
- length += n2;
18500
- }
18501
- need6(length === Number(before.size) && sameStat(before, fstatSync12(fd, { bigint: true })) && sameStat(before, lstatSync21(file, { bigint: true })), "INPUT_CHANGED");
18502
- const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
18503
- return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash16("sha256").update(content).digest("hex") };
18504
- } finally {
18505
- closeSync12(fd);
18506
- }
18507
- }
18508
- function unchanged2(before) {
18509
- const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
18510
- need6(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
18511
- }
18512
-
18513
18706
  // src/lib/codex-hook-additions-review.ts
18514
18707
  var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
18515
18708
  var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
@@ -18521,7 +18714,7 @@ var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
18521
18714
  var proofs2 = new WeakMap;
18522
18715
  function assertCodexHookDiscoveryRecovery(binding, witness) {
18523
18716
  const proof = proofs2.get(witness);
18524
- need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
18717
+ need5(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
18525
18718
  unchanged2(proof.before);
18526
18719
  unchanged2(proof.current);
18527
18720
  proof.recheckAdditions();
@@ -18530,7 +18723,7 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
18530
18723
  if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
18531
18724
  return false;
18532
18725
  assertCodexHookDiscoveryRecovery(binding, witness);
18533
- need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
18726
+ need5(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
18534
18727
  return true;
18535
18728
  }
18536
18729
 
@@ -45935,6 +46128,7 @@ export {
45935
46128
  planPluginAdmission,
45936
46129
  planClaudeStopHookUpdate,
45937
46130
  planClaudePreToolUseHookUpdate,
46131
+ planClaudeManagedHookProjection,
45938
46132
  planClaudeHookEventsUpdate,
45939
46133
  planAgentSettingsWitnessUpgrade,
45940
46134
  planAgentIntegration,