@hasna/skills 0.10.34 → 0.10.35

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.35",
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) {
@@ -14208,10 +14208,10 @@ function upgradeCodexSettingsWitnessV3(previous, reviewedSettingsPath) {
14208
14208
  }
14209
14209
 
14210
14210
  // src/lib/agent-integration.ts
14211
- import { createHash as createHash12, randomUUID as randomUUID2 } from "crypto";
14211
+ import { createHash as createHash13, randomUUID as randomUUID2 } from "crypto";
14212
14212
  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";
14213
+ 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";
14214
+ import { dirname as dirname12, join as join18, resolve as resolve11, relative as relative5, isAbsolute as isAbsolute11, sep as sep5 } from "path";
14215
14215
  import { homedir as homedir2 } from "os";
14216
14216
 
14217
14217
  // src/lib/managed-policy.ts
@@ -14713,15 +14713,66 @@ var SUMI_SKILL_PERMISSIONS = [
14713
14713
  { action: "skill", resource: "skills-cli", effect: "allow" }
14714
14714
  ];
14715
14715
 
14716
+ // src/lib/codex-hook-trust-files.ts
14717
+ 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";
14718
+ import { createHash as createHash12 } from "crypto";
14719
+ import { dirname as dirname11 } from "path";
14720
+ function need5(value, code) {
14721
+ if (!value)
14722
+ throw new Error(`CODEX_HOOK_TRUST_${code}`);
14723
+ }
14724
+ var sameStat = (a, b) => ["dev", "ino", "uid", "nlink", "mode", "size", "mtimeNs", "ctimeNs"].every((key) => a[key] === b[key]);
14725
+ function safeParents(file) {
14726
+ for (let p = dirname11(file);; p = dirname11(p)) {
14727
+ const s = lstatSync16(p);
14728
+ need5(s.isDirectory() && !s.isSymbolicLink(), "UNSAFE_PARENT");
14729
+ need5((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
14730
+ if (dirname11(p) === p)
14731
+ break;
14732
+ }
14733
+ }
14734
+ function snapshot(file, privateFile = false, options = {}) {
14735
+ const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
14736
+ safeParents(file);
14737
+ const initial = lstatSync16(file, { bigint: true });
14738
+ 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");
14739
+ need5(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
14740
+ const fd = openSync8(file, constants7.O_RDONLY | constants7.O_NOFOLLOW | constants7.O_NONBLOCK);
14741
+ try {
14742
+ const before = fstatSync8(fd, { bigint: true });
14743
+ need5(sameStat(initial, before), "INPUT_CHANGED");
14744
+ const bytes = Buffer.alloc(Number(before.size) + 1);
14745
+ let length = 0;
14746
+ while (length < bytes.length) {
14747
+ const n2 = readSync6(fd, bytes, length, bytes.length - length, null);
14748
+ if (!n2)
14749
+ break;
14750
+ length += n2;
14751
+ }
14752
+ need5(length === Number(before.size) && sameStat(before, fstatSync8(fd, { bigint: true })) && sameStat(before, lstatSync16(file, { bigint: true })), "INPUT_CHANGED");
14753
+ const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
14754
+ return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash12("sha256").update(content).digest("hex") };
14755
+ } finally {
14756
+ closeSync8(fd);
14757
+ }
14758
+ }
14759
+ function unchanged2(before) {
14760
+ const after = snapshot(before.file, before.privateFile, before.readOnlyPackage ? { readOnlyPackage: true } : {});
14761
+ need5(sameStat(before.stat, after.stat) && after.sha256 === before.sha256, "INPUT_CHANGED");
14762
+ }
14763
+
14716
14764
  // src/lib/agent-integration.ts
14717
- var sha3 = (data) => createHash12("sha256").update(data).digest("hex");
14765
+ var sha3 = (data) => createHash13("sha256").update(data).digest("hex");
14718
14766
  var ROOTS = NATIVE_SKILL_ROOTS;
14719
- function hasNoDarwinAcl(fd) {
14767
+ function hasTrustedDarwinAcl(fd, allowDenyOnly = false) {
14720
14768
  try {
14721
- const { dlopen, read: read2 } = __require("bun:ffi");
14769
+ const { dlopen, read: read2, ptr } = __require("bun:ffi");
14722
14770
  const library = dlopen("/usr/lib/libSystem.B.dylib", {
14723
14771
  acl_get_fd: { args: ["i32"], returns: "ptr" },
14724
14772
  acl_free: { args: ["ptr"], returns: "i32" },
14773
+ acl_valid: { args: ["ptr"], returns: "i32" },
14774
+ acl_get_entry: { args: ["ptr", "i32", "ptr"], returns: "i32" },
14775
+ acl_get_tag_type: { args: ["ptr", "ptr"], returns: "i32" },
14725
14776
  __error: { args: [], returns: "ptr" }
14726
14777
  });
14727
14778
  try {
@@ -14730,8 +14781,23 @@ function hasNoDarwinAcl(fd) {
14730
14781
  return false;
14731
14782
  const acl = library.symbols.acl_get_fd(fd);
14732
14783
  if (acl) {
14733
- library.symbols.acl_free(acl);
14734
- return false;
14784
+ try {
14785
+ if (!allowDenyOnly || library.symbols.acl_valid(acl) !== 0)
14786
+ return false;
14787
+ const entry = new BigUint64Array(1), tag = new Int32Array(1);
14788
+ for (let count = 0;count <= 128; count++) {
14789
+ const status = library.symbols.acl_get_entry(acl, count === 0 ? 0 : -1, ptr(entry));
14790
+ if (status !== 0) {
14791
+ return status === -1 && read2.i32(errno) === 22 && library.symbols.acl_valid(acl) === 0;
14792
+ }
14793
+ const value = read2.ptr(ptr(entry));
14794
+ if (count === 128 || !value || library.symbols.acl_get_tag_type(value, ptr(tag)) !== 0 || tag[0] !== 2)
14795
+ return false;
14796
+ }
14797
+ return false;
14798
+ } finally {
14799
+ library.symbols.acl_free(acl);
14800
+ }
14735
14801
  }
14736
14802
  return (acl === null || acl === 0) && read2.i32(errno) === 2;
14737
14803
  } finally {
@@ -14747,11 +14813,11 @@ function isSystemRootAlias(path) {
14747
14813
  const target = `/private${path}`, link = `private${path}`;
14748
14814
  const identity4 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}`;
14749
14815
  try {
14750
- const before = lstatSync16(path, { bigint: true });
14816
+ const before = lstatSync17(path, { bigint: true });
14751
14817
  if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync4(path) !== link)
14752
14818
  return false;
14753
14819
  const directories = ["/", "/private", target].map((directory) => {
14754
- const stat = lstatSync16(directory, { bigint: true });
14820
+ const stat = lstatSync17(directory, { bigint: true });
14755
14821
  if (!stat.isDirectory() || stat.uid !== 0n)
14756
14822
  throw new Error("Untrusted system alias directory");
14757
14823
  if (directory !== "/private/tmp") {
@@ -14759,23 +14825,23 @@ function isSystemRootAlias(path) {
14759
14825
  throw new Error("Writable system alias directory");
14760
14826
  } else if ((stat.mode & 0o1000n) === 0n)
14761
14827
  throw new Error("Unprotected system temporary directory");
14762
- const fd = openSync8(directory, constants7.O_RDONLY | constants7.O_DIRECTORY | constants7.O_NOFOLLOW);
14828
+ const fd = openSync9(directory, constants8.O_RDONLY | constants8.O_DIRECTORY | constants8.O_NOFOLLOW);
14763
14829
  try {
14764
- if (identity4(fstatSync8(fd, { bigint: true })) !== identity4(stat) || !hasNoDarwinAcl(fd) || identity4(fstatSync8(fd, { bigint: true })) !== identity4(stat))
14830
+ if (identity4(fstatSync9(fd, { bigint: true })) !== identity4(stat) || !hasTrustedDarwinAcl(fd) || identity4(fstatSync9(fd, { bigint: true })) !== identity4(stat))
14765
14831
  throw new Error("Unverified system alias directory ACL");
14766
14832
  } finally {
14767
- closeSync8(fd);
14833
+ closeSync9(fd);
14768
14834
  }
14769
14835
  return { directory, identity: identity4(stat) };
14770
14836
  });
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);
14837
+ 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
14838
  } catch {
14773
14839
  return false;
14774
14840
  }
14775
14841
  }
14776
14842
  function canonicalSystemPath(path) {
14777
14843
  const absolute = resolve11(path), root = `/${absolute.split(sep5)[1]}`;
14778
- if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync16(root, { throwIfNoEntry: false })?.isSymbolicLink())
14844
+ if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync17(root, { throwIfNoEntry: false })?.isSymbolicLink())
14779
14845
  return absolute;
14780
14846
  if (!isSystemRootAlias(root))
14781
14847
  throw new Error(`Refusing symlink path: ${root}`);
@@ -14784,9 +14850,9 @@ function canonicalSystemPath(path) {
14784
14850
  function assertSafePath(path) {
14785
14851
  let cursor = resolve11(path);
14786
14852
  while (true) {
14787
- if (lstatSync16(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
14853
+ if (lstatSync17(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
14788
14854
  throw new Error(`Refusing symlink path: ${cursor}`);
14789
- const parent = dirname11(cursor);
14855
+ const parent = dirname12(cursor);
14790
14856
  if (parent === cursor)
14791
14857
  return;
14792
14858
  cursor = parent;
@@ -14795,12 +14861,12 @@ function assertSafePath(path) {
14795
14861
  function readRootAlias(home, agent) {
14796
14862
  assertSafePath(home);
14797
14863
  const realHome = realpathSync7(home), alias = join18(realHome, `.${agent}`);
14798
- const stat = lstatSync16(alias, { throwIfNoEntry: false, bigint: true });
14864
+ const stat = lstatSync17(alias, { throwIfNoEntry: false, bigint: true });
14799
14865
  if (!stat?.isSymbolicLink())
14800
14866
  throw new Error(`Agent root alias changed after planning: ${alias}`);
14801
- const link = readlinkSync4(alias), targetPath = resolve11(dirname11(alias), link);
14867
+ const link = readlinkSync4(alias), targetPath = resolve11(dirname12(alias), link);
14802
14868
  assertSafePath(targetPath);
14803
- const targetStat = lstatSync16(targetPath, { throwIfNoEntry: false });
14869
+ const targetStat = lstatSync17(targetPath, { throwIfNoEntry: false });
14804
14870
  if (!targetStat?.isDirectory())
14805
14871
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
14806
14872
  const target = realpathSync7(targetPath), inside = relative5(realHome, target);
@@ -14813,7 +14879,7 @@ function rootAliases(home, allowed = false) {
14813
14879
  return [];
14814
14880
  const result = [];
14815
14881
  for (const agent of ["claude", "codex"])
14816
- if (lstatSync16(join18(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
14882
+ if (lstatSync17(join18(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
14817
14883
  result.push(readRootAlias(home, agent));
14818
14884
  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
14885
  throw new Error("Agent root alias targets must not overlap");
@@ -14848,7 +14914,7 @@ function disabledCodexSkillPaths(config, aliases) {
14848
14914
  continue;
14849
14915
  settings.set(document2, [...settings.get(document2) ?? [], entry.enabled === false]);
14850
14916
  }
14851
- return [...settings].filter(([, values]) => values.length === 1 && values[0] === true).map(([document2]) => dirname11(document2));
14917
+ return [...settings].filter(([, values]) => values.length === 1 && values[0] === true).map(([document2]) => dirname12(document2));
14852
14918
  }
14853
14919
  function recheckRootAliases(aliases) {
14854
14920
  for (const binding of aliases) {
@@ -14861,35 +14927,35 @@ function recheckRootAliases(aliases) {
14861
14927
  }
14862
14928
  function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
14863
14929
  assertSafePath(path);
14864
- const before = lstatSync16(path);
14930
+ const before = lstatSync17(path);
14865
14931
  if (!before.isFile() || before.size > maximum)
14866
14932
  throw new Error("Unsupported or oversized native skill file");
14867
- const descriptor = openSync8(path, constants7.O_RDONLY | constants7.O_NOFOLLOW | constants7.O_NONBLOCK);
14933
+ const descriptor = openSync9(path, constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK);
14868
14934
  try {
14869
- const opened = fstatSync8(descriptor);
14935
+ const opened = fstatSync9(descriptor);
14870
14936
  if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size)
14871
14937
  throw new Error("Native skill file changed while reading");
14872
14938
  const bytes = Buffer.allocUnsafe(before.size + 1);
14873
14939
  let length = 0;
14874
14940
  while (length < bytes.length) {
14875
- const count = readSync6(descriptor, bytes, length, bytes.length - length, null);
14941
+ const count = readSync7(descriptor, bytes, length, bytes.length - length, null);
14876
14942
  if (!count)
14877
14943
  break;
14878
14944
  length += count;
14879
14945
  }
14880
- const after = fstatSync8(descriptor), current = lstatSync16(path);
14946
+ const after = fstatSync9(descriptor), current = lstatSync17(path);
14881
14947
  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
14948
  throw new Error("Native skill file changed while reading");
14883
14949
  return bytes.subarray(0, length);
14884
14950
  } finally {
14885
- closeSync8(descriptor);
14951
+ closeSync9(descriptor);
14886
14952
  }
14887
14953
  }
14888
14954
  function treeHash(root) {
14889
- const hash2 = createHash12("sha256");
14955
+ const hash2 = createHash13("sha256");
14890
14956
  let bytes = 0, files = 0;
14891
14957
  function visit2(path) {
14892
- const stat = lstatSync16(path);
14958
+ const stat = lstatSync17(path);
14893
14959
  if (stat.isSymbolicLink())
14894
14960
  throw new Error(`Refusing symlink in skill: ${path}`);
14895
14961
  const rel = relative5(root, path);
@@ -14921,7 +14987,7 @@ function selectNativeMigrationTargets(inventory, manifest) {
14921
14987
  if (!isAbsolute11(target.projectRoot) || resolve11(target.projectRoot) !== target.projectRoot)
14922
14988
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
14923
14989
  const projectRoot = canonicalSystemPath(target.projectRoot), normalized = relative5(projectRoot, resolve11(projectRoot, target.path));
14924
- if (!existsSync9(projectRoot) || !lstatSync16(projectRoot).isDirectory())
14990
+ if (!existsSync9(projectRoot) || !lstatSync17(projectRoot).isDirectory())
14925
14991
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
14926
14992
  assertSafePath(projectRoot);
14927
14993
  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 +15016,7 @@ function projectAncestorDirectories(projects) {
14950
15016
  if (depth >= 100)
14951
15017
  throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
14952
15018
  directories.add(path);
14953
- const parent = dirname11(path);
15019
+ const parent = dirname12(path);
14954
15020
  if (parent === path)
14955
15021
  break;
14956
15022
  path = parent;
@@ -14996,12 +15062,12 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
14996
15062
  let reviewedCacheAliasFound = false;
14997
15063
  function verifiedSiblingCacheAlias(agent, path, parent) {
14998
15064
  const link = readlinkSync4(path), lexicalTarget = resolve11(parent, link);
14999
- if (isAbsolute11(link) ? link !== lexicalTarget : dirname11(link) !== ".")
15065
+ if (isAbsolute11(link) ? link !== lexicalTarget : dirname12(link) !== ".")
15000
15066
  return false;
15001
15067
  const target = canonicalSystemPath(lexicalTarget);
15002
- if (dirname11(target) !== parent)
15068
+ if (dirname12(target) !== parent)
15003
15069
  return false;
15004
- if (!lstatSync16(target, { throwIfNoEntry: false })?.isDirectory())
15070
+ if (!lstatSync17(target, { throwIfNoEntry: false })?.isDirectory())
15005
15071
  return false;
15006
15072
  const scan = cacheScans.get(target);
15007
15073
  if (!scan?.complete || scan.entries > maxAliasProofEntries)
@@ -15026,7 +15092,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
15026
15092
  return scan;
15027
15093
  if (!existsSync9(path))
15028
15094
  return scan;
15029
- const stat = lstatSync16(path);
15095
+ const stat = lstatSync17(path);
15030
15096
  if (stat.isFile()) {
15031
15097
  if (agent === "sumi" && depth === 1 && path.endsWith(".md"))
15032
15098
  throw new Error("NATIVE_SKILL_DRIFT: flat Sumi skill documents require a dedicated migration adapter");
@@ -15075,7 +15141,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
15075
15141
  if (isVendor && !options.includeVendor)
15076
15142
  continue;
15077
15143
  const child = join18(path, name);
15078
- if (isVendor && lstatSync16(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
15144
+ if (isVendor && lstatSync17(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
15079
15145
  const target = statSync8(child, { throwIfNoEntry: false });
15080
15146
  if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child, path)) {
15081
15147
  scan.entries++;
@@ -15409,6 +15475,171 @@ function planAgentIntegration(options) {
15409
15475
  recheckRootAliases(aliases);
15410
15476
  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
15477
  }
15478
+ function hookProjectionRefusal(reason) {
15479
+ throw new Error(`CLAUDE_MANAGED_HOOK_${reason}`);
15480
+ }
15481
+ function projectedHookAcl(path, observed) {
15482
+ if (process.platform !== "darwin")
15483
+ return;
15484
+ const identity4 = (stat) => `${stat.dev}:${stat.ino}:${stat.ctimeNs}:${stat.uid}:${stat.mode}:${stat.nlink}:${stat.size}:${stat.mtimeNs}`;
15485
+ const fd = openSync9(path, constants8.O_RDONLY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK | (observed.isDirectory() ? constants8.O_DIRECTORY : 0));
15486
+ try {
15487
+ 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))
15488
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15489
+ } finally {
15490
+ closeSync9(fd);
15491
+ }
15492
+ }
15493
+ function projectedHookExecutable(command, expectedResolved) {
15494
+ try {
15495
+ if (!isAbsolute11(command) || /[\0\r\n]/.test(command) || resolve11(command) !== command)
15496
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15497
+ command = canonicalSystemPath(command);
15498
+ const parents2 = (file = command) => {
15499
+ const result = [];
15500
+ for (let path = dirname12(file);; path = dirname12(path)) {
15501
+ const stat = lstatSync17(path, { bigint: true });
15502
+ if (!stat.isDirectory() || (stat.mode & 0o022n) !== 0n && !(stat.uid === 0n && (stat.mode & 0o1000n) !== 0n))
15503
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15504
+ projectedHookAcl(path, stat);
15505
+ result.push(`${path}:${stat.dev}:${stat.ino}:${stat.uid}:${stat.mode}`);
15506
+ if (dirname12(path) === path)
15507
+ break;
15508
+ }
15509
+ return result;
15510
+ };
15511
+ const parentIdentity = parents2(), link = lstatSync17(command, { bigint: true });
15512
+ if (link.uid !== BigInt(process.getuid()) || !link.isFile() && !link.isSymbolicLink())
15513
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15514
+ const linkIdentity = (stat) => `${stat.dev}:${stat.ino}:${stat.mode}:${stat.uid}:${stat.ctimeNs}`;
15515
+ const resolved = realpathSync7(command);
15516
+ if (expectedResolved !== undefined && resolved !== expectedResolved)
15517
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15518
+ const resolvedParentIdentity = parents2(resolved);
15519
+ const manifest = snapshot(join18(dirname12(dirname12(resolved)), "package.json"), false, { readOnlyPackage: true });
15520
+ projectedHookAcl(manifest.file, manifest.stat);
15521
+ const pkg = JSON.parse(manifest.text);
15522
+ 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"))
15523
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15524
+ const cli = snapshot(resolved, false, { readOnlyPackage: true });
15525
+ projectedHookAcl(cli.file, cli.stat);
15526
+ if ((cli.stat.mode & 0o100n) === 0n)
15527
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15528
+ const recheck = () => {
15529
+ try {
15530
+ if (realpathSync7(command) !== resolved || linkIdentity(lstatSync17(command, { bigint: true })) !== linkIdentity(link) || !isDeepStrictEqual4(parents2(), parentIdentity) || !isDeepStrictEqual4(parents2(resolved), resolvedParentIdentity))
15531
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15532
+ projectedHookAcl(cli.file, cli.stat);
15533
+ projectedHookAcl(manifest.file, manifest.stat);
15534
+ unchanged2(cli);
15535
+ unchanged2(manifest);
15536
+ } catch {
15537
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15538
+ }
15539
+ };
15540
+ recheck();
15541
+ return { resolved, sha256: cli.sha256, manifestSha256: manifest.sha256, recheck };
15542
+ } catch {
15543
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15544
+ }
15545
+ }
15546
+ function planClaudeManagedHookProjection(options) {
15547
+ if (typeof options.targetSettings !== "string" || Buffer.byteLength(options.targetSettings) > 1024 * 1024)
15548
+ hookProjectionRefusal("TARGET_INVALID");
15549
+ const targetSha256 = sha3(options.targetSettings);
15550
+ if (!/^[a-f0-9]{64}$/.test(options.expectedTargetSha256) || options.expectedTargetSha256 !== targetSha256)
15551
+ hookProjectionRefusal("TARGET_CHANGED");
15552
+ let target;
15553
+ try {
15554
+ target = jsonObject(options.targetSettings, "target");
15555
+ } catch {
15556
+ hookProjectionRefusal("TARGET_INVALID");
15557
+ }
15558
+ const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15559
+ const policyPath = join18(dataDir2, "agent-policy.json");
15560
+ let policyBytes, settingsBytes, settingsPath, command, profile;
15561
+ try {
15562
+ policyBytes = readNativeBytes(policyPath, 1024 * 1024);
15563
+ const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
15564
+ if (!snapshot2 || snapshot2.text !== policyBytes.toString("utf8"))
15565
+ hookProjectionRefusal("SOURCE_UNVERIFIED");
15566
+ const binding = snapshot2.value.bridge;
15567
+ settingsPath = canonicalAgentPath(join18(home, AGENT_ADAPTERS.claude.config), binding?.rootAliases ?? []);
15568
+ settingsBytes = readNativeBytes(settingsPath, 1024 * 1024);
15569
+ command = binding?.commands?.claude;
15570
+ profile = binding?.profiles?.claude;
15571
+ if (typeof command !== "string" || typeof profile !== "string")
15572
+ hookProjectionRefusal("SOURCE_UNVERIFIED");
15573
+ assertManagedAgentBridge("claude", { home, dataDir: dataDir2, projectDir: options.projectDir, profileId: profile });
15574
+ } catch {
15575
+ hookProjectionRefusal("SOURCE_UNVERIFIED");
15576
+ }
15577
+ const executable = projectedHookExecutable(command);
15578
+ const sourceSha256 = sha3(JSON.stringify({
15579
+ home,
15580
+ policyPath,
15581
+ policy: sha3(policyBytes),
15582
+ settingsPath,
15583
+ settings: sha3(settingsBytes),
15584
+ executable: { path: executable.resolved, sha256: executable.sha256, manifestSha256: executable.manifestSha256 }
15585
+ }));
15586
+ if (options.expectedSourceSha256 !== undefined && options.expectedSourceSha256 !== sourceSha256)
15587
+ hookProjectionRefusal("SOURCE_CHANGED");
15588
+ const replacements = [];
15589
+ const observed = [executable];
15590
+ if (target.hooks !== undefined) {
15591
+ if (!target.hooks || typeof target.hooks !== "object" || Array.isArray(target.hooks))
15592
+ hookProjectionRefusal("TARGET_INVALID");
15593
+ for (const event of [...AGENT_ADAPTERS.claude.events, "PreToolUse"]) {
15594
+ const groups = target.hooks[event];
15595
+ if (groups === undefined)
15596
+ continue;
15597
+ if (!Array.isArray(groups))
15598
+ hookProjectionRefusal("TARGET_INVALID");
15599
+ let recognized = 0;
15600
+ for (const [groupIndex, group] of groups.entries()) {
15601
+ if (!group || !Array.isArray(group.hooks))
15602
+ hookProjectionRefusal("TARGET_INVALID");
15603
+ for (const [hookIndex, hook] of group.hooks.entries()) {
15604
+ if (hook?.type !== "command" || typeof hook.command !== "string")
15605
+ continue;
15606
+ if (!hook.command.includes("hook user-prompt --agent claude"))
15607
+ continue;
15608
+ 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);
15609
+ if (!match || match[3] !== event)
15610
+ hookProjectionRefusal("UNRECOGNIZED");
15611
+ const oldCommand = match[1].slice(1, -1).replaceAll("'\\''", "'");
15612
+ let rendered;
15613
+ try {
15614
+ rendered = renderAgentHookCommand(oldCommand, "claude", match[2], event);
15615
+ } catch {
15616
+ hookProjectionRefusal("UNRECOGNIZED");
15617
+ }
15618
+ if (rendered !== hook.command)
15619
+ hookProjectionRefusal("UNRECOGNIZED");
15620
+ const legacy = projectedHookExecutable(oldCommand, executable.resolved);
15621
+ if (legacy.resolved !== executable.resolved || legacy.sha256 !== executable.sha256 || legacy.manifestSha256 !== executable.manifestSha256)
15622
+ hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
15623
+ observed.push(legacy);
15624
+ if (++recognized > 1)
15625
+ hookProjectionRefusal("AMBIGUOUS");
15626
+ const replacement = renderAgentHookCommand(command, "claude", profile, event);
15627
+ if (replacement !== hook.command)
15628
+ replacements.push({ event, groupIndex, hookIndex, beforeSha256: sha3(hook.command), command: replacement });
15629
+ }
15630
+ }
15631
+ }
15632
+ }
15633
+ try {
15634
+ if (!readNativeBytes(policyPath, 1024 * 1024).equals(policyBytes) || !readNativeBytes(settingsPath, 1024 * 1024).equals(settingsBytes))
15635
+ hookProjectionRefusal("SOURCE_CHANGED");
15636
+ } catch {
15637
+ hookProjectionRefusal("SOURCE_CHANGED");
15638
+ }
15639
+ for (const item of observed)
15640
+ item.recheck();
15641
+ return { schema: "hasna.claude-managed-hook-projection.v1", targetSha256, sourceSha256, replacements };
15642
+ }
15412
15643
  function planClaudeStopHookUpdate(options) {
15413
15644
  return planClaudeHookEventsUpdate({ ...options, events: ["Stop"] });
15414
15645
  }
@@ -15441,10 +15672,10 @@ function planClaudeHookEventsUpdate(options) {
15441
15672
  const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15442
15673
  const policyPath = join18(dataDir2, "agent-policy.json");
15443
15674
  assertSafePath(policyPath);
15444
- const snapshot = readManagedSkillPolicySnapshot(dataDir2);
15445
- if (!snapshot)
15675
+ const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
15676
+ if (!snapshot2)
15446
15677
  return null;
15447
- const policy = snapshot.value, bridge = policy.bridge;
15678
+ const policy = snapshot2.value, bridge = policy.bridge;
15448
15679
  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
15680
  throw new Error("Invalid managed bridge for Claude settings update");
15450
15681
  }
@@ -15452,7 +15683,7 @@ function planClaudeHookEventsUpdate(options) {
15452
15683
  recheckRootAliases(aliases);
15453
15684
  const path = canonicalAgentPath(join18(home, ".claude", "settings.json"), aliases);
15454
15685
  assertSafePath(path);
15455
- const before = lstatSync16(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
15686
+ const before = lstatSync17(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
15456
15687
  if ((before === null ? "absent" : sha3(before)) !== options.expectedSettingsSha256)
15457
15688
  throw new Error("Claude settings preimage changed");
15458
15689
  assertClaudeHookEventsReplacement(before, options.replacement, options.events);
@@ -15482,7 +15713,7 @@ function planClaudeHookEventsUpdate(options) {
15482
15713
  const nextPolicy = { ...policy, bridge: { ...bridge, discovery: Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) } };
15483
15714
  const changes = before === options.replacement ? [] : [{ path, before, after: options.replacement }];
15484
15715
  if (JSON.stringify(nextPolicy) !== JSON.stringify(policy))
15485
- changes.push({ path: policyPath, before: snapshot.text, after: serializeManagedSkillPolicy(nextPolicy) });
15716
+ changes.push({ path: policyPath, before: snapshot2.text, after: serializeManagedSkillPolicy(nextPolicy) });
15486
15717
  recheckRootAliases(aliases);
15487
15718
  const nativeSkills = inventoryNativeSkills(home, {
15488
15719
  agents: consumers,
@@ -15497,7 +15728,7 @@ function planClaudeHookEventsUpdate(options) {
15497
15728
  profileId: policy.profileId ?? "default",
15498
15729
  changes,
15499
15730
  nativeSkills,
15500
- observedPolicy: { path: policyPath, before: snapshot.text },
15731
+ observedPolicy: { path: policyPath, before: snapshot2.text },
15501
15732
  discoveryBefore: discoveries,
15502
15733
  discoveryAfter,
15503
15734
  rootAliases: aliases,
@@ -15506,10 +15737,10 @@ function planClaudeHookEventsUpdate(options) {
15506
15737
  }
15507
15738
  function atomicWrite2(path, content) {
15508
15739
  assertSafePath(path);
15509
- mkdirSync5(dirname11(path), { recursive: true, mode: 448 });
15740
+ mkdirSync5(dirname12(path), { recursive: true, mode: 448 });
15510
15741
  const temporary = `${path}.skills-${randomUUID2()}`;
15511
15742
  try {
15512
- writeFileSync4(temporary, content, { mode: 384, flag: "wx" });
15743
+ writeFileSync5(temporary, content, { mode: 384, flag: "wx" });
15513
15744
  renameSync2(temporary, path);
15514
15745
  } finally {
15515
15746
  if (existsSync9(temporary))
@@ -15520,10 +15751,10 @@ function planAgentSettingsWitnessUpgrade(options) {
15520
15751
  if (options.targetCodexVersion !== undefined && (options.agent !== "codex" || ![2, 3].includes(options.targetCodexVersion)))
15521
15752
  throw new Error("Invalid Codex witness target version");
15522
15753
  const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15523
- const snapshot = readManagedSkillPolicySnapshot(dataDir2);
15524
- if (!snapshot || sha3(snapshot.text) !== options.expectedPolicySha256)
15754
+ const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
15755
+ if (!snapshot2 || sha3(snapshot2.text) !== options.expectedPolicySha256)
15525
15756
  throw new Error("Managed policy preimage changed");
15526
- const policy = snapshot.value, aliases = policy.bridge?.rootAliases ?? [];
15757
+ const policy = snapshot2.value, aliases = policy.bridge?.rootAliases ?? [];
15527
15758
  recheckRootAliases(aliases);
15528
15759
  const binding = policy.bridge?.discovery?.[options.agent];
15529
15760
  if (!binding || binding.agent !== options.agent || binding.method !== "reviewed")
@@ -15567,7 +15798,7 @@ function planAgentSettingsWitnessUpgrade(options) {
15567
15798
  const replacement = { ...binding, sources: rebound };
15568
15799
  verifyAgentDiscovery(replacement);
15569
15800
  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 })) } };
15801
+ 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
15802
  }
15572
15803
  function applyAgentIntegration(plan) {
15573
15804
  for (const source of plan.observedNativeSources ?? [])
@@ -15620,7 +15851,7 @@ function applyAgentIntegration(plan) {
15620
15851
  for (const [index, change] of plan.changes.entries())
15621
15852
  if (change.before !== null) {
15622
15853
  const backup = join18(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
15623
- writeFileSync4(backup, change.before, { mode: 384, flag: "wx" });
15854
+ writeFileSync5(backup, change.before, { mode: 384, flag: "wx" });
15624
15855
  if (readFileSync12(backup, "utf8") !== change.before)
15625
15856
  throw new Error("Native configuration preservation readback failed");
15626
15857
  backups.push(backup);
@@ -15630,7 +15861,7 @@ function applyAgentIntegration(plan) {
15630
15861
  recheckRootAliases(aliases);
15631
15862
  if (currentText(change.path) !== change.before)
15632
15863
  throw new Error(`Configuration changed after planning: ${change.path}`);
15633
- for (let directory = dirname11(change.path);!existsSync9(directory); directory = dirname11(directory))
15864
+ for (let directory = dirname12(change.path);!existsSync9(directory); directory = dirname12(directory))
15634
15865
  createdDirectories.add(directory);
15635
15866
  const after = change.after;
15636
15867
  atomicWrite2(change.path, after);
@@ -15681,11 +15912,11 @@ function applyAgentIntegration(plan) {
15681
15912
  }
15682
15913
  function syncArchiveDirectory(path) {
15683
15914
  assertSafePath(path);
15684
- const descriptor = openSync8(path, constants7.O_RDONLY | constants7.O_DIRECTORY | constants7.O_NOFOLLOW | constants7.O_NONBLOCK);
15915
+ const descriptor = openSync9(path, constants8.O_RDONLY | constants8.O_DIRECTORY | constants8.O_NOFOLLOW | constants8.O_NONBLOCK);
15685
15916
  try {
15686
- fsyncSync2(descriptor);
15917
+ fsyncSync3(descriptor);
15687
15918
  } finally {
15688
- closeSync8(descriptor);
15919
+ closeSync9(descriptor);
15689
15920
  }
15690
15921
  }
15691
15922
  function writeArchiveJournal(path, value) {
@@ -15693,18 +15924,18 @@ function writeArchiveJournal(path, value) {
15693
15924
  const temporary = `${path}.skills-${randomUUID2()}`;
15694
15925
  let descriptor;
15695
15926
  try {
15696
- descriptor = openSync8(temporary, constants7.O_WRONLY | constants7.O_CREAT | constants7.O_EXCL | constants7.O_NOFOLLOW, 384);
15697
- writeFileSync4(descriptor, `${JSON.stringify(value)}
15927
+ descriptor = openSync9(temporary, constants8.O_WRONLY | constants8.O_CREAT | constants8.O_EXCL | constants8.O_NOFOLLOW, 384);
15928
+ writeFileSync5(descriptor, `${JSON.stringify(value)}
15698
15929
  `);
15699
- fsyncSync2(descriptor);
15700
- closeSync8(descriptor);
15930
+ fsyncSync3(descriptor);
15931
+ closeSync9(descriptor);
15701
15932
  descriptor = undefined;
15702
15933
  renameSync2(temporary, path);
15703
- syncArchiveDirectory(dirname11(path));
15934
+ syncArchiveDirectory(dirname12(path));
15704
15935
  } finally {
15705
15936
  if (descriptor !== undefined)
15706
- closeSync8(descriptor);
15707
- if (lstatSync16(temporary, { throwIfNoEntry: false }))
15937
+ closeSync9(descriptor);
15938
+ if (lstatSync17(temporary, { throwIfNoEntry: false }))
15708
15939
  unlinkSync3(temporary);
15709
15940
  }
15710
15941
  }
@@ -15738,7 +15969,7 @@ function archiveNativeSkills(inventory, options) {
15738
15969
  const moved = [];
15739
15970
  const verifyArchive = (entry) => {
15740
15971
  assertSafePath(entry.archive);
15741
- const stat = lstatSync16(entry.archive);
15972
+ const stat = lstatSync17(entry.archive);
15742
15973
  if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
15743
15974
  throw new Error("Archive type changed");
15744
15975
  if ((entry.discoveryOnly ? sha3(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
@@ -15746,14 +15977,14 @@ function archiveNativeSkills(inventory, options) {
15746
15977
  };
15747
15978
  assertSafePath(archiveRoot);
15748
15979
  const created = [];
15749
- for (let path = archiveRoot;!existsSync9(path); path = dirname11(path))
15980
+ for (let path = archiveRoot;!existsSync9(path); path = dirname12(path))
15750
15981
  created.push(path);
15751
15982
  mkdirSync5(archiveRoot, { recursive: true, mode: 448 });
15752
15983
  try {
15753
15984
  for (const directory of created)
15754
15985
  syncArchiveDirectory(directory);
15755
15986
  if (created.length)
15756
- syncArchiveDirectory(dirname11(created.at(-1)));
15987
+ syncArchiveDirectory(dirname12(created.at(-1)));
15757
15988
  writeArchiveJournal(receiptPath, journal);
15758
15989
  for (const [index, entry] of selected.entries()) {
15759
15990
  recheckRootAliases(aliases);
@@ -15765,7 +15996,7 @@ function archiveNativeSkills(inventory, options) {
15765
15996
  writeArchiveJournal(receiptPath, journal);
15766
15997
  renameSync2(move.source, move.archive);
15767
15998
  moved.push(move);
15768
- syncArchiveDirectory(dirname11(move.source));
15999
+ syncArchiveDirectory(dirname12(move.source));
15769
16000
  syncArchiveDirectory(archiveRoot);
15770
16001
  verifyArchive(move);
15771
16002
  move.status = "archived";
@@ -15786,7 +16017,7 @@ function archiveNativeSkills(inventory, options) {
15786
16017
  recheckRootAliases(aliases);
15787
16018
  assertSafePath(entry.source);
15788
16019
  assertSafePath(entry.archive);
15789
- if (lstatSync16(entry.source, { throwIfNoEntry: false })) {
16020
+ if (lstatSync17(entry.source, { throwIfNoEntry: false })) {
15790
16021
  entry.status = "recovery-required";
15791
16022
  entry.conflict = "source-occupied";
15792
16023
  continue;
@@ -15800,10 +16031,10 @@ function archiveNativeSkills(inventory, options) {
15800
16031
  }
15801
16032
  if (entry.discoveryOnly) {
15802
16033
  linkSync2(entry.archive, entry.source);
15803
- syncArchiveDirectory(dirname11(entry.source));
16034
+ syncArchiveDirectory(dirname12(entry.source));
15804
16035
  unlinkSync3(entry.archive);
15805
16036
  } else {
15806
- if (lstatSync16(entry.source, { throwIfNoEntry: false })) {
16037
+ if (lstatSync17(entry.source, { throwIfNoEntry: false })) {
15807
16038
  entry.status = "recovery-required";
15808
16039
  entry.conflict = "source-occupied";
15809
16040
  continue;
@@ -15811,7 +16042,7 @@ function archiveNativeSkills(inventory, options) {
15811
16042
  renameSync2(entry.archive, entry.source);
15812
16043
  }
15813
16044
  entry.status = "restored";
15814
- syncArchiveDirectory(dirname11(entry.source));
16045
+ syncArchiveDirectory(dirname12(entry.source));
15815
16046
  syncArchiveDirectory(archiveRoot);
15816
16047
  } catch {
15817
16048
  entry.status = "recovery-required";
@@ -15836,10 +16067,10 @@ function assertManagedAgentBridge(agent, options = {}) {
15836
16067
  function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
15837
16068
  const home = resolve11(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
15838
16069
  assertSafePath(join18(dataDir2, "agent-policy.json"));
15839
- const snapshot = readManagedSkillPolicySnapshot(dataDir2);
15840
- if (!snapshot)
16070
+ const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
16071
+ if (!snapshot2)
15841
16072
  throw new Error("NATIVE_SKILL_DRIFT: install the Skills bridge with skills hook install");
15842
- const binding = snapshot.value.bridge;
16073
+ const binding = snapshot2.value.bridge;
15843
16074
  if (!binding || binding.version !== CLI_BRIDGE_VERSION || binding.digest !== CLI_BRIDGE_DIGEST || binding.home !== home || !Array.isArray(binding.agents) || !binding.agents.includes(agent))
15844
16075
  throw new Error("NATIVE_SKILL_DRIFT: the managed Skills bridge binding is missing or incompatible; run skills hook install");
15845
16076
  const aliases = binding.rootAliases ?? [];
@@ -15980,7 +16211,7 @@ function isSkillsOwnershipMarker(marker) {
15980
16211
  function hasSkillsOwnershipMarker(dir) {
15981
16212
  const path = join19(dir, SYNC_MARKER_FILE);
15982
16213
  try {
15983
- if (!lstatSync17(path).isFile())
16214
+ if (!lstatSync18(path).isFile())
15984
16215
  return false;
15985
16216
  const marker = JSON.parse(readFileSync13(path, "utf8"));
15986
16217
  return isSkillsOwnershipMarker(marker);
@@ -16206,7 +16437,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
16206
16437
  const action = dirExists ? "update" : "create";
16207
16438
  if (options.dryRun)
16208
16439
  return { action, path: skillMdPath };
16209
- const parentDir = dirname12(dir);
16440
+ const parentDir = dirname13(dir);
16210
16441
  mkdirSync6(parentDir, { recursive: true });
16211
16442
  const transactionDir = mkdtempSync(join19(parentDir, `.hasna-skills-write-${basename6(dir)}-`));
16212
16443
  const candidateDir = join19(transactionDir, "candidate");
@@ -16228,10 +16459,10 @@ function writeManagedSkillDir(dir, skillMd, options) {
16228
16459
  } else {
16229
16460
  mkdirSync6(candidateDir, { recursive: true });
16230
16461
  }
16231
- writeFileSync5(candidateSkillMdPath, skillMd.endsWith(`
16462
+ writeFileSync6(candidateSkillMdPath, skillMd.endsWith(`
16232
16463
  `) ? skillMd : `${skillMd}
16233
16464
  `);
16234
- writeFileSync5(candidateMarkerPath, `${JSON.stringify(marker, null, 2)}
16465
+ writeFileSync6(candidateMarkerPath, `${JSON.stringify(marker, null, 2)}
16235
16466
  `);
16236
16467
  if (dirExists) {
16237
16468
  originalMoved = true;
@@ -16311,7 +16542,7 @@ function resolveSkillAlias(name) {
16311
16542
  }
16312
16543
 
16313
16544
  // src/lib/project-state.ts
16314
- import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
16545
+ import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
16315
16546
  import { join as join20 } from "path";
16316
16547
  var VALID_PIN_SOURCES = [
16317
16548
  "official",
@@ -16360,7 +16591,7 @@ function saveProjectConfig(config, targetDir = process.cwd()) {
16360
16591
  const dir = getProjectStateDir(targetDir);
16361
16592
  mkdirSync7(dir, { recursive: true });
16362
16593
  const normalized = normalizeProjectConfig({ ...config, updatedAt: new Date().toISOString() });
16363
- writeFileSync6(getProjectConfigPath(targetDir), JSON.stringify(normalized, null, 2) + `
16594
+ writeFileSync7(getProjectConfigPath(targetDir), JSON.stringify(normalized, null, 2) + `
16364
16595
  `);
16365
16596
  }
16366
16597
  function pinProjectSkill(name, details = {}, targetDir = process.cwd()) {
@@ -17179,7 +17410,7 @@ function exactProfileSelection(spec, profile) {
17179
17410
  }
17180
17411
 
17181
17412
  // src/lib/plugin-projection.ts
17182
- import { createHash as createHash13 } from "crypto";
17413
+ import { createHash as createHash14 } from "crypto";
17183
17414
  import { posix } from "path";
17184
17415
  var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
17185
17416
  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 +17421,7 @@ function pluginNeed(value, message) {
17190
17421
  if (!value)
17191
17422
  pluginRefusal(message);
17192
17423
  }
17193
- var pluginHash = (bytes) => createHash13("sha256").update(bytes).digest("hex");
17424
+ var pluginHash = (bytes) => createHash14("sha256").update(bytes).digest("hex");
17194
17425
  function pluginObject(value) {
17195
17426
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
17196
17427
  }
@@ -17378,16 +17609,16 @@ function buildPluginProjection(entries) {
17378
17609
  }
17379
17610
 
17380
17611
  // 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;
17612
+ import { createHash as createHash15, randomUUID as randomUUID3 } from "crypto";
17613
+ 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";
17614
+ import { dirname as dirname14, isAbsolute as isAbsolute12, join as join25, parse as parse5, resolve as resolve12 } from "path";
17615
+ var READ_FLAGS = constants9.O_RDONLY | constants9.O_NOFOLLOW | constants9.O_NONBLOCK;
17616
+ var WRITE_FLAGS = constants9.O_WRONLY | constants9.O_CREAT | constants9.O_EXCL | constants9.O_NOFOLLOW | constants9.O_NONBLOCK;
17386
17617
  var uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined;
17387
17618
  var missing = (error) => Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
17388
17619
  function stat(path) {
17389
17620
  try {
17390
- return lstatSync18(path, { bigint: true });
17621
+ return lstatSync19(path, { bigint: true });
17391
17622
  } catch (error) {
17392
17623
  if (missing(error))
17393
17624
  return null;
@@ -17406,7 +17637,7 @@ function same3(left, right, identityOnly = false) {
17406
17637
  }
17407
17638
  function ancestors(path, create = false) {
17408
17639
  canonical5(path);
17409
- const result = [], parent = dirname13(path), filesystemRoot = parse5(parent).root;
17640
+ const result = [], parent = dirname14(path), filesystemRoot = parse5(parent).root;
17410
17641
  let current = filesystemRoot;
17411
17642
  for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
17412
17643
  if (segment)
@@ -17434,7 +17665,7 @@ function verifyAncestors(chain, identityOnly = true) {
17434
17665
  }
17435
17666
  function privateParent(path) {
17436
17667
  const chain = ancestors(path, true), parent = chain.at(-1);
17437
- pluginNeed(parent && parent.path === dirname13(path), "Plugin storage parent is unavailable");
17668
+ pluginNeed(parent && parent.path === dirname14(path), "Plugin storage parent is unavailable");
17438
17669
  owner(parent.stat);
17439
17670
  pluginNeed((parent.stat.mode & 0o7777n) === 0o700n, "Plugin storage parent must have mode 0700");
17440
17671
  return chain;
@@ -17463,19 +17694,19 @@ function readBytes(path, maximum, kind) {
17463
17694
  fileShape(before, maximum, kind);
17464
17695
  let fd;
17465
17696
  try {
17466
- fd = openSync9(path, READ_FLAGS);
17467
- const opened = fstatSync9(fd, { bigint: true });
17697
+ fd = openSync10(path, READ_FLAGS);
17698
+ const opened = fstatSync10(fd, { bigint: true });
17468
17699
  same3(before, opened);
17469
17700
  fileShape(opened, maximum, kind);
17470
17701
  const bytes = new Uint8Array(Number(opened.size));
17471
17702
  let offset = 0;
17472
17703
  while (offset < bytes.byteLength) {
17473
- const count = readSync7(fd, bytes, offset, bytes.byteLength - offset, null);
17704
+ const count = readSync8(fd, bytes, offset, bytes.byteLength - offset, null);
17474
17705
  pluginNeed(count > 0, "Plugin file changed while being read");
17475
17706
  offset += count;
17476
17707
  }
17477
- pluginNeed(readSync7(fd, new Uint8Array(1), 0, 1, null) === 0, "Plugin file grew while being read");
17478
- same3(opened, fstatSync9(fd, { bigint: true }));
17708
+ pluginNeed(readSync8(fd, new Uint8Array(1), 0, 1, null) === 0, "Plugin file grew while being read");
17709
+ same3(opened, fstatSync10(fd, { bigint: true }));
17479
17710
  same3(opened, stat(path));
17480
17711
  verifyAncestors(chain);
17481
17712
  return bytes;
@@ -17485,7 +17716,7 @@ function readBytes(path, maximum, kind) {
17485
17716
  return pluginRefusal("Cannot safely read plugin file");
17486
17717
  } finally {
17487
17718
  if (fd !== undefined)
17488
- closeSync9(fd);
17719
+ closeSync10(fd);
17489
17720
  }
17490
17721
  }
17491
17722
  function exactPaths(entries) {
@@ -17595,16 +17826,16 @@ function verifyPluginTree(root, witnesses) {
17595
17826
  function writeExclusive(path, bytes, mode) {
17596
17827
  let fd, created;
17597
17828
  try {
17598
- fd = openSync9(path, WRITE_FLAGS, mode);
17829
+ fd = openSync10(path, WRITE_FLAGS, mode);
17599
17830
  fchmodSync(fd, mode);
17600
- created = fstatSync9(fd, { bigint: true });
17831
+ created = fstatSync10(fd, { bigint: true });
17601
17832
  let offset = 0;
17602
17833
  while (offset < bytes.byteLength) {
17603
17834
  const count = writeSync2(fd, bytes, offset, bytes.byteLength - offset);
17604
17835
  pluginNeed(count > 0, "Plugin file write did not progress");
17605
17836
  offset += count;
17606
17837
  }
17607
- fsyncSync3(fd);
17838
+ fsyncSync4(fd);
17608
17839
  } catch (error) {
17609
17840
  if (created) {
17610
17841
  same3(created, stat(path), true);
@@ -17613,7 +17844,7 @@ function writeExclusive(path, bytes, mode) {
17613
17844
  throw error;
17614
17845
  } finally {
17615
17846
  if (fd !== undefined)
17616
- closeSync9(fd);
17847
+ closeSync10(fd);
17617
17848
  }
17618
17849
  }
17619
17850
  function materializePluginTree(root, entries) {
@@ -17716,20 +17947,20 @@ function pluginExecutableDigest(path) {
17716
17947
  fileShape(before, maximum, "executable");
17717
17948
  let fd;
17718
17949
  try {
17719
- fd = openSync9(path, READ_FLAGS);
17720
- const opened = fstatSync9(fd, { bigint: true });
17950
+ fd = openSync10(path, READ_FLAGS);
17951
+ const opened = fstatSync10(fd, { bigint: true });
17721
17952
  same3(before, opened);
17722
17953
  fileShape(opened, maximum, "executable");
17723
- const hash2 = createHash14("sha256"), buffer = new Uint8Array(64 * 1024);
17954
+ const hash2 = createHash15("sha256"), buffer = new Uint8Array(64 * 1024);
17724
17955
  let remaining = Number(opened.size);
17725
17956
  while (remaining > 0) {
17726
- const count = readSync7(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
17957
+ const count = readSync8(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
17727
17958
  pluginNeed(count > 0, "Plugin executable changed during hashing");
17728
17959
  hash2.update(buffer.subarray(0, count));
17729
17960
  remaining -= count;
17730
17961
  }
17731
- pluginNeed(readSync7(fd, buffer, 0, 1, null) === 0, "Plugin executable grew during hashing");
17732
- same3(opened, fstatSync9(fd, { bigint: true }));
17962
+ pluginNeed(readSync8(fd, buffer, 0, 1, null) === 0, "Plugin executable grew during hashing");
17963
+ same3(opened, fstatSync10(fd, { bigint: true }));
17733
17964
  same3(opened, stat(path));
17734
17965
  verifyAncestors(chain);
17735
17966
  return `sha256:${hash2.digest("hex")}`;
@@ -17739,7 +17970,7 @@ function pluginExecutableDigest(path) {
17739
17970
  return pluginRefusal("Cannot safely hash plugin executable");
17740
17971
  } finally {
17741
17972
  if (fd !== undefined)
17742
- closeSync9(fd);
17973
+ closeSync10(fd);
17743
17974
  }
17744
17975
  }
17745
17976
 
@@ -18149,29 +18380,29 @@ function validateManagedPluginWitnesses(value) {
18149
18380
  function safe3(path) {
18150
18381
  pluginText(path);
18151
18382
  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))
18383
+ for (let at = path;; at = dirname15(at)) {
18384
+ pluginNeed(!lstatSync20(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
18385
+ if (at === dirname15(at))
18155
18386
  break;
18156
18387
  }
18157
18388
  }
18158
18389
  function readRegistry(path) {
18159
18390
  safe3(path);
18160
- const initial = lstatSync19(path);
18391
+ const initial = lstatSync20(path);
18161
18392
  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);
18393
+ const fd = openSync11(path, constants10.O_RDONLY | constants10.O_NOFOLLOW | constants10.O_NONBLOCK);
18163
18394
  try {
18164
- const opened = fstatSync10(fd);
18395
+ const opened = fstatSync11(fd);
18165
18396
  pluginNeed(opened.isFile() && opened.ino === initial.ino && opened.dev === initial.dev, "Plugin registry changed during open");
18166
18397
  const bytes = Buffer.alloc(1024 * 1024 + 1);
18167
18398
  let length = 0;
18168
18399
  while (length < bytes.length) {
18169
- const count = readSync8(fd, bytes, length, bytes.length - length, null);
18400
+ const count = readSync9(fd, bytes, length, bytes.length - length, null);
18170
18401
  if (!count)
18171
18402
  break;
18172
18403
  length += count;
18173
18404
  }
18174
- const after = fstatSync10(fd), current = lstatSync19(path);
18405
+ const after = fstatSync11(fd), current = lstatSync20(path);
18175
18406
  safe3(path);
18176
18407
  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
18408
  let value;
@@ -18183,7 +18414,7 @@ function readRegistry(path) {
18183
18414
  pluginNeed(pluginObject(value) && value.version === 2 && pluginObject(value.plugins), "Unsupported native plugin registry schema");
18184
18415
  return { value, rawDigest: pluginHash(bytes.subarray(0, length)) };
18185
18416
  } finally {
18186
- closeSync10(fd);
18417
+ closeSync11(fd);
18187
18418
  }
18188
18419
  }
18189
18420
  function nativeVersionMatches(version, receipt) {
@@ -18192,7 +18423,7 @@ function nativeVersionMatches(version, receipt) {
18192
18423
  }
18193
18424
  function hashManagedPluginRegistry(path, managed) {
18194
18425
  validateManagedPluginWitnesses(managed);
18195
- const snapshot = readRegistry(path), plugins = snapshot.value.plugins;
18426
+ const snapshot2 = readRegistry(path), plugins = snapshot2.value.plugins;
18196
18427
  const seen = new Set, verified = new Set;
18197
18428
  let verifiedBytes = 0;
18198
18429
  for (const witness of managed) {
@@ -18201,7 +18432,7 @@ function hashManagedPluginRegistry(path, managed) {
18201
18432
  seen.add(target.pluginId);
18202
18433
  const rows = plugins[target.pluginId];
18203
18434
  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);
18435
+ const [name, market] = target.pluginId.split("@"), cacheRoot = join27(dirname15(path), "cache", market, name);
18205
18436
  const scopes = new Set, approvals = new Map, currentFiles = new Map;
18206
18437
  plugins[target.pluginId] = rows.map((row) => {
18207
18438
  pluginNeed(pluginObject(row), "Invalid managed plugin registration");
@@ -18244,7 +18475,7 @@ function hashManagedPluginRegistry(path, managed) {
18244
18475
  return Object.fromEntries(Object.entries(row).filter(([key]) => !["version", "installPath", "sourceProducerPath", "previousProducerPaths", "lastUpdated"].includes(key)));
18245
18476
  });
18246
18477
  safe3(cacheRoot);
18247
- const before = lstatSync19(cacheRoot);
18478
+ const before = lstatSync20(cacheRoot);
18248
18479
  pluginNeed(before.isDirectory(), "Invalid managed plugin cache root");
18249
18480
  const directory = opendirSync5(cacheRoot), names = [];
18250
18481
  try {
@@ -18267,64 +18498,64 @@ function hashManagedPluginRegistry(path, managed) {
18267
18498
  }
18268
18499
  for (const row of rows)
18269
18500
  pluginNeed(names.includes(row.version), "Managed native plugin installation is missing");
18270
- const after = lstatSync19(cacheRoot);
18501
+ const after = lstatSync20(cacheRoot);
18271
18502
  safe3(cacheRoot);
18272
18503
  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
18504
  }
18274
- pluginNeed(readRegistry(path).rawDigest === snapshot.rawDigest, "Native plugin registry changed during content verification");
18275
- return pluginHash(JSON.stringify(snapshot.value));
18505
+ pluginNeed(readRegistry(path).rawDigest === snapshot2.rawDigest, "Native plugin registry changed during content verification");
18506
+ return pluginHash(JSON.stringify(snapshot2.value));
18276
18507
  }
18277
18508
  function captureManagedPluginRegistry(path, managedPlugins) {
18278
18509
  return { path, hashMode: "claude-plugin-registry", managedPlugins, sha256: hashManagedPluginRegistry(path, managedPlugins) };
18279
18510
  }
18280
18511
 
18281
18512
  // 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";
18513
+ import { createHash as createHash16 } from "crypto";
18514
+ import { closeSync as closeSync12, constants as constants11, fstatSync as fstatSync12, lstatSync as lstatSync21, openSync as openSync12, readSync as readSync10 } from "fs";
18515
+ import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute15, resolve as resolve15 } from "path";
18285
18516
  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) {
18517
+ function need6(value, reason) {
18287
18518
  if (!value)
18288
18519
  throw new Error(`Claude marketplace registry ${reason}`);
18289
18520
  }
18290
18521
  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");
18522
+ 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
18523
  }
18293
18524
  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
18525
  function parents2(path) {
18295
18526
  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");
18527
+ for (let at = dirname16(path);; at = dirname16(at)) {
18528
+ const stat2 = lstatSync21(at, { bigint: true });
18529
+ need6(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
18299
18530
  result.push([at, stat2]);
18300
- if (at === dirname15(at))
18531
+ if (at === dirname16(at))
18301
18532
  return result;
18302
18533
  }
18303
18534
  }
18304
18535
  function readRegistry2(path, budget) {
18305
18536
  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);
18537
+ need6(basename8(path) === "known_marketplaces.json", "requires known_marketplaces.json");
18538
+ need6(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
18539
+ const ancestors2 = parents2(path), initial = lstatSync21(path, { bigint: true });
18540
+ need6(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
18541
+ need6(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
18542
+ const fd = openSync12(path, constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK);
18312
18543
  try {
18313
- const opened = fstatSync11(fd, { bigint: true });
18314
- need5(opened.isFile() && sameFile2(initial, opened), "changed during open");
18544
+ const opened = fstatSync12(fd, { bigint: true });
18545
+ need6(opened.isFile() && sameFile2(initial, opened), "changed during open");
18315
18546
  const bytes = Buffer.alloc(Number(opened.size) + 1);
18316
18547
  let length = 0;
18317
18548
  while (length < bytes.length) {
18318
- const count = readSync9(fd, bytes, length, bytes.length - length, null);
18549
+ const count = readSync10(fd, bytes, length, bytes.length - length, null);
18319
18550
  if (!count)
18320
18551
  break;
18321
18552
  length += count;
18322
18553
  }
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");
18554
+ const after = fstatSync12(fd, { bigint: true }), current = lstatSync21(path, { bigint: true });
18555
+ need6(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
18325
18556
  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");
18557
+ const now = lstatSync21(ancestor, { bigint: true });
18558
+ need6(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
18328
18559
  }
18329
18560
  budget.remaining -= length;
18330
18561
  try {
@@ -18333,7 +18564,7 @@ function readRegistry2(path, budget) {
18333
18564
  throw new Error("Claude marketplace registry is not strict UTF-8");
18334
18565
  }
18335
18566
  } finally {
18336
- closeSync11(fd);
18567
+ closeSync12(fd);
18337
18568
  }
18338
18569
  }
18339
18570
  function parse6(text2) {
@@ -18344,7 +18575,7 @@ function parse6(text2) {
18344
18575
  at++;
18345
18576
  };
18346
18577
  function string() {
18347
- need5(text2[at] === '"', "contains invalid JSON");
18578
+ need6(text2[at] === '"', "contains invalid JSON");
18348
18579
  const start = at++;
18349
18580
  while (at < text2.length) {
18350
18581
  const character = text2[at++];
@@ -18355,7 +18586,7 @@ function parse6(text2) {
18355
18586
  } catch {
18356
18587
  throw new Error("Claude marketplace registry contains invalid JSON string");
18357
18588
  }
18358
- need5(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
18589
+ need6(value2.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.stringCharacters, "exceeds its string limit");
18359
18590
  return value2;
18360
18591
  }
18361
18592
  if (character === "\\")
@@ -18364,7 +18595,7 @@ function parse6(text2) {
18364
18595
  throw new Error("Claude marketplace registry contains an unterminated JSON string");
18365
18596
  }
18366
18597
  function value(depth) {
18367
- need5(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
18598
+ need6(depth <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.depth && ++nodes <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.nodes, "exceeds its nesting or node limit");
18368
18599
  space();
18369
18600
  const character = text2[at];
18370
18601
  if (character === '"')
@@ -18378,17 +18609,17 @@ function parse6(text2) {
18378
18609
  if (object5) {
18379
18610
  space();
18380
18611
  const key = string();
18381
- need5(!keys.has(key), "contains a duplicate JSON key");
18612
+ need6(!keys.has(key), "contains a duplicate JSON key");
18382
18613
  keys.add(key);
18383
18614
  space();
18384
- need5(text2[at++] === ":", "contains invalid JSON");
18615
+ need6(text2[at++] === ":", "contains invalid JSON");
18385
18616
  entries.push([key, value(depth + 1)]);
18386
18617
  } else
18387
18618
  items.push(value(depth + 1));
18388
18619
  space();
18389
18620
  if (text2[at] === end)
18390
18621
  break;
18391
- need5(text2[at++] === ",", "contains invalid JSON");
18622
+ need6(text2[at++] === ",", "contains invalid JSON");
18392
18623
  }
18393
18624
  at++;
18394
18625
  return object5 ? { kind: "object", entries } : { kind: "array", items };
@@ -18400,14 +18631,14 @@ function parse6(text2) {
18400
18631
  }
18401
18632
  numberPattern.lastIndex = at;
18402
18633
  const number = numberPattern.exec(text2);
18403
- need5(number, "contains invalid JSON");
18634
+ need6(number, "contains invalid JSON");
18404
18635
  at += number[0].length;
18405
18636
  return { kind: "number", value: number[0] };
18406
18637
  }
18407
18638
  const root2 = value(0);
18408
18639
  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");
18640
+ need6(at === text2.length && root2.kind === "object", "requires a JSON object without trailing content");
18641
+ need6(root2.entries.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.rows, "exceeds its marketplace row limit");
18411
18642
  return root2;
18412
18643
  }
18413
18644
  function exactKeys(value, keys) {
@@ -18415,7 +18646,7 @@ function exactKeys(value, keys) {
18415
18646
  }
18416
18647
  var field = (object5, key) => object5.entries.find(([name]) => name === key)?.[1];
18417
18648
  function textField(value) {
18418
- need5(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
18649
+ need6(value?.kind === "string" && value.value.length > 0 && !/[\x00-\x1f\x7f]/.test(value.value), "contains an invalid recognized field");
18419
18650
  return value.value;
18420
18651
  }
18421
18652
  function normalizeRow(row, version = 1) {
@@ -18423,7 +18654,7 @@ function normalizeRow(row, version = 1) {
18423
18654
  if (version !== 2 || !exactKeys(row, ["source", "installLocation", "lastUpdated", "autoUpdate"]))
18424
18655
  return;
18425
18656
  const autoUpdate = field(row, "autoUpdate");
18426
- need5(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
18657
+ need6(autoUpdate?.kind === "literal" && (autoUpdate.value === "true" || autoUpdate.value === "false"), "contains an invalid automatic-update preference");
18427
18658
  }
18428
18659
  const source = field(row, "source");
18429
18660
  if (source?.kind !== "object")
@@ -18439,77 +18670,29 @@ function normalizeRow(row, version = 1) {
18439
18670
  absolutePath3(sourceLocation);
18440
18671
  absolutePath3(textField(field(row, "installLocation")));
18441
18672
  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");
18673
+ 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
18674
  const millis = Date.parse(updated);
18444
- need5(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
18675
+ need6(Number.isFinite(millis) && new Date(millis).toISOString() === updated, "contains an invalid recognized update timestamp");
18445
18676
  row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
18446
18677
  }
18447
18678
  function captureClaudeMarketplaceRegistry(path, budget = { remaining: 256 * 1024 * 1024 }) {
18448
18679
  const value = parse6(readRegistry2(path, budget));
18449
18680
  for (const [, row] of value.entries)
18450
18681
  normalizeRow(row);
18451
- const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
18682
+ const sha2562 = createHash16("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
18452
18683
  return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
18453
18684
  }
18454
18685
  function captureClaudeMarketplaceRegistryV2(path, budget = { remaining: 256 * 1024 * 1024 }) {
18455
18686
  const value = parse6(readRegistry2(path, budget));
18456
18687
  for (const [, row] of value.entries)
18457
18688
  normalizeRow(row, 2);
18458
- const sha2562 = createHash15("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
18689
+ const sha2562 = createHash16("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
18459
18690
  return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
18460
18691
  }
18461
18692
 
18462
18693
  // src/lib/codex-hook-discovery-recovery.ts
18463
18694
  import { createHash as createHash17 } from "crypto";
18464
18695
 
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
18696
  // src/lib/codex-hook-additions-review.ts
18514
18697
  var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
18515
18698
  var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
@@ -18521,7 +18704,7 @@ var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
18521
18704
  var proofs2 = new WeakMap;
18522
18705
  function assertCodexHookDiscoveryRecovery(binding, witness) {
18523
18706
  const proof = proofs2.get(witness);
18524
- need6(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
18707
+ need5(!!proof && binding.agent === "codex" && witness.discoverySha256 === sha4(JSON.stringify(binding)), "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
18525
18708
  unchanged2(proof.before);
18526
18709
  unchanged2(proof.current);
18527
18710
  proof.recheckAdditions();
@@ -18530,7 +18713,7 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
18530
18713
  if (!witness || source.path !== witness.configPath || source.hashMode !== "bytes")
18531
18714
  return false;
18532
18715
  assertCodexHookDiscoveryRecovery(binding, witness);
18533
- need6(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
18716
+ need5(source.sha256 === witness.beforeSha256 && currentDigest === witness.currentSha256, "RECONCILE_DISCOVERY_RECOVERY_CHANGED");
18534
18717
  return true;
18535
18718
  }
18536
18719
 
@@ -45935,6 +46118,7 @@ export {
45935
46118
  planPluginAdmission,
45936
46119
  planClaudeStopHookUpdate,
45937
46120
  planClaudePreToolUseHookUpdate,
46121
+ planClaudeManagedHookProjection,
45938
46122
  planClaudeHookEventsUpdate,
45939
46123
  planAgentSettingsWitnessUpgrade,
45940
46124
  planAgentIntegration,