@hasna/hooks 0.10.8 → 0.11.0

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
@@ -4762,12 +4762,12 @@ __export(exports_store, {
4762
4762
  checkScriptHash: () => checkScriptHash,
4763
4763
  LockFileError: () => LockFileError
4764
4764
  });
4765
- import { createHash as createHash2 } from "crypto";
4766
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync } from "fs";
4765
+ import { createHash as createHash3 } from "crypto";
4766
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2, renameSync } from "fs";
4767
4767
  import { readFile } from "fs/promises";
4768
4768
  import { randomBytes } from "crypto";
4769
4769
  function sha256Of(input) {
4770
- return createHash2("sha256").update(input).digest("hex");
4770
+ return createHash3("sha256").update(input).digest("hex");
4771
4771
  }
4772
4772
  async function sha256File(path) {
4773
4773
  const buf = await readFile(path);
@@ -4828,11 +4828,11 @@ function assertLockShape(parsed, path) {
4828
4828
  }
4829
4829
  function readLock() {
4830
4830
  const path = getLockPath();
4831
- if (!existsSync7(path))
4831
+ if (!existsSync8(path))
4832
4832
  return { hooks: {} };
4833
4833
  let raw;
4834
4834
  try {
4835
- raw = readFileSync4(path, "utf-8");
4835
+ raw = readFileSync5(path, "utf-8");
4836
4836
  } catch {
4837
4837
  throw new LockFileError(`hooks.lock (${path}) could not be read. Repair: fix permissions or move the file aside and run 'hooks sync'.`);
4838
4838
  }
@@ -4898,15 +4898,15 @@ function removeHookFromStore(name) {
4898
4898
  function checkScriptHash(name, actual) {
4899
4899
  const db = storeDb();
4900
4900
  const record = db ? getHookRecord(db, name) : null;
4901
- const pin = getPinnedHook(name);
4902
- const expected = record?.sha256 ?? pin?.sha256 ?? null;
4901
+ const pin2 = getPinnedHook(name);
4902
+ const expected = record?.sha256 ?? pin2?.sha256 ?? null;
4903
4903
  if (expected !== null && expected !== actual) {
4904
4904
  return { ok: false, pinned: true, expected, actual, name };
4905
4905
  }
4906
4906
  if (expected === null) {
4907
4907
  const now2 = new Date().toISOString();
4908
- const version = pin?.version ?? record?.version ?? "0.0.0";
4909
- const source = pin?.source ?? record?.source_type ?? "local";
4908
+ const version = pin2?.version ?? record?.version ?? "0.0.0";
4909
+ const source = pin2?.source ?? record?.source_type ?? "local";
4910
4910
  if (db) {
4911
4911
  upsertHookRecord(db, {
4912
4912
  name,
@@ -4923,9 +4923,9 @@ function checkScriptHash(name, actual) {
4923
4923
  if (db) {
4924
4924
  upsertHookRecord(db, {
4925
4925
  name,
4926
- version: record?.version ?? pin?.version ?? "0.0.0",
4926
+ version: record?.version ?? pin2?.version ?? "0.0.0",
4927
4927
  sha256: expected,
4928
- source_type: record?.source_type ?? pin?.source ?? "local",
4928
+ source_type: record?.source_type ?? pin2?.source ?? "local",
4929
4929
  last_verified_at: now
4930
4930
  });
4931
4931
  }
@@ -4935,7 +4935,7 @@ async function verifyScriptHash(name, scriptPath) {
4935
4935
  return checkScriptHash(name, await sha256File(scriptPath));
4936
4936
  }
4937
4937
  function retrustHook(name, scriptPath, version, source) {
4938
- const actual = sha256Of(readFileSync4(scriptPath));
4938
+ const actual = sha256Of(readFileSync5(scriptPath));
4939
4939
  const db = storeDb();
4940
4940
  if (db) {
4941
4941
  upsertHookRecord(db, {
@@ -4974,12 +4974,12 @@ __export(exports_profiles, {
4974
4974
  deleteProfile: () => deleteProfile,
4975
4975
  createProfile: () => createProfile
4976
4976
  });
4977
- import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync8, readdirSync as readdirSync8, rmSync, cpSync as cpSync2 } from "fs";
4978
- import { join as join12 } from "path";
4977
+ import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync8, readdirSync as readdirSync8, rmSync, cpSync as cpSync2 } from "fs";
4978
+ import { join as join11 } from "path";
4979
4979
  function getProfilesDir() {
4980
- const newDir = join12(getEffectiveDataRoot(), "profiles");
4981
- const oldDir = join12(getHomeDir(), ".hooks", "profiles");
4982
- if (!existsSync11(newDir) && existsSync11(oldDir)) {
4980
+ const newDir = join11(getEffectiveDataRoot(), "profiles");
4981
+ const oldDir = join11(getHomeDir(), ".hooks", "profiles");
4982
+ if (!existsSync12(newDir) && existsSync12(oldDir)) {
4983
4983
  mkdirSync9(newDir, { recursive: true });
4984
4984
  cpSync2(oldDir, newDir, { recursive: true });
4985
4985
  }
@@ -4987,12 +4987,12 @@ function getProfilesDir() {
4987
4987
  }
4988
4988
  function ensureProfilesDir() {
4989
4989
  const dir = getProfilesDir();
4990
- if (!existsSync11(dir)) {
4990
+ if (!existsSync12(dir)) {
4991
4991
  mkdirSync9(dir, { recursive: true });
4992
4992
  }
4993
4993
  }
4994
4994
  function profilePath(id) {
4995
- return join12(getProfilesDir(), `${id}.json`);
4995
+ return join11(getProfilesDir(), `${id}.json`);
4996
4996
  }
4997
4997
  function shortUuid() {
4998
4998
  return crypto.randomUUID().slice(0, 8);
@@ -5018,23 +5018,23 @@ function createProfile(input2) {
5018
5018
  function getProfile(id) {
5019
5019
  const path = profilePath(id);
5020
5020
  try {
5021
- if (!existsSync11(path))
5021
+ if (!existsSync12(path))
5022
5022
  return null;
5023
- return JSON.parse(readFileSync9(path, "utf-8"));
5023
+ return JSON.parse(readFileSync8(path, "utf-8"));
5024
5024
  } catch {
5025
5025
  return null;
5026
5026
  }
5027
5027
  }
5028
5028
  function listProfiles() {
5029
5029
  const dir = getProfilesDir();
5030
- if (!existsSync11(dir))
5030
+ if (!existsSync12(dir))
5031
5031
  return [];
5032
5032
  try {
5033
5033
  const files = readdirSync8(dir).filter((f) => f.endsWith(".json"));
5034
5034
  const profiles = [];
5035
5035
  for (const file2 of files) {
5036
5036
  try {
5037
- const content = readFileSync9(join12(dir, file2), "utf-8");
5037
+ const content = readFileSync8(join11(dir, file2), "utf-8");
5038
5038
  profiles.push(JSON.parse(content));
5039
5039
  } catch {}
5040
5040
  }
@@ -5057,7 +5057,7 @@ function updateProfile(id, data) {
5057
5057
  }
5058
5058
  function deleteProfile(id) {
5059
5059
  const path = profilePath(id);
5060
- if (!existsSync11(path))
5060
+ if (!existsSync12(path))
5061
5061
  return false;
5062
5062
  try {
5063
5063
  rmSync(path);
@@ -5087,7 +5087,7 @@ function importProfiles(profiles) {
5087
5087
  continue;
5088
5088
  }
5089
5089
  const path = profilePath(profile.agent_id);
5090
- if (existsSync11(path)) {
5090
+ if (existsSync12(path)) {
5091
5091
  skipped++;
5092
5092
  continue;
5093
5093
  }
@@ -5110,9 +5110,9 @@ import { hostname as osHostname } from "os";
5110
5110
  import { isAbsolute as isAbsolute7, join as join22 } from "path";
5111
5111
  import { createRequire as createRequire2 } from "module";
5112
5112
  import { fileURLToPath as fileURLToPath22 } from "url";
5113
- import { closeSync as closeSync4, fstatSync as fstatSync3, lstatSync as lstatSync7, openSync as openSync4, readFileSync as readFileSync12, realpathSync as realpathSync7, statSync } from "fs";
5113
+ import { closeSync as closeSync5, fstatSync as fstatSync4, lstatSync as lstatSync7, openSync as openSync5, readFileSync as readFileSync9, realpathSync as realpathSync7, statSync } from "fs";
5114
5114
  import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
5115
- import { basename as basename5, dirname as dirname11, join as join13, relative, sep as sep3 } from "path";
5115
+ import { basename as basename5, dirname as dirname11, join as join12, relative, sep as sep3 } from "path";
5116
5116
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
5117
5117
  function envToken2(name) {
5118
5118
  return name.toUpperCase().replace(/-/g, "_");
@@ -5187,15 +5187,15 @@ function o(e2, r2, t2) {
5187
5187
  }
5188
5188
  function entryPoint(directory2) {
5189
5189
  const root = realpathSync7(directory2);
5190
- const fd = openSync4(join13(root, "package.json"), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
5190
+ const fd = openSync5(join12(root, "package.json"), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
5191
5191
  let pkg;
5192
5192
  try {
5193
- const stat2 = fstatSync3(fd);
5193
+ const stat2 = fstatSync4(fd);
5194
5194
  if (!stat2.isFile() || stat2.size > MAX_PACKAGE_BYTES2)
5195
5195
  throw new Error("Invalid Secrets package metadata");
5196
- pkg = JSON.parse(readFileSync12(fd, "utf8"));
5196
+ pkg = JSON.parse(readFileSync9(fd, "utf8"));
5197
5197
  } finally {
5198
- closeSync4(fd);
5198
+ closeSync5(fd);
5199
5199
  }
5200
5200
  if (!pkg || typeof pkg !== "object" || !("name" in pkg) || pkg.name !== PACKAGE) {
5201
5201
  throw new Error("Invalid Secrets package identity");
@@ -5207,7 +5207,7 @@ function entryPoint(directory2) {
5207
5207
  if (/[\\%?#\0]/.test(target) || parts.some((part) => !part || part === "." || part === ".." || part === "node_modules")) {
5208
5208
  throw new Error("Invalid Secrets import export");
5209
5209
  }
5210
- const file2 = realpathSync7(join13(root, ...parts));
5210
+ const file2 = realpathSync7(join12(root, ...parts));
5211
5211
  const within = relative(root, file2);
5212
5212
  if (!within || within === ".." || within.startsWith(`..${sep3}`) || !statSync(file2).isFile()) {
5213
5213
  throw new Error("Secrets import export escapes its package or is not a file");
@@ -5218,7 +5218,7 @@ function resolveInstalledSecrets(parentUrl) {
5218
5218
  let directory2 = dirname11(fileURLToPath3(parentUrl));
5219
5219
  for (;; ) {
5220
5220
  if (basename5(directory2) !== "node_modules") {
5221
- const candidate = join13(directory2, "node_modules", PACKAGE);
5221
+ const candidate = join12(directory2, "node_modules", PACKAGE);
5222
5222
  let absent = false;
5223
5223
  try {
5224
5224
  lstatSync7(candidate);
@@ -5288,9 +5288,9 @@ function parseEnvFile(text3) {
5288
5288
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
5289
5289
  continue;
5290
5290
  let value = withoutExport.slice(equals + 1).trim();
5291
- const quote3 = value[0];
5292
- if (quote3 === '"' || quote3 === "'") {
5293
- if (value.length < 2 || !value.endsWith(quote3)) {
5291
+ const quote4 = value[0];
5292
+ if (quote4 === '"' || quote4 === "'") {
5293
+ if (value.length < 2 || !value.endsWith(quote4)) {
5294
5294
  unusable.add(key);
5295
5295
  continue;
5296
5296
  }
@@ -5740,9 +5740,9 @@ async function completePointerCredential(name, pointerResolution, env = process.
5740
5740
  }
5741
5741
  let client;
5742
5742
  try {
5743
- const bootstrap = resolveCredential("secrets", secretsEnv);
5744
- if (!bootstrap || bootstrap.tier === "pointer") {
5745
- throw new CredentialResolutionError("secrets", "The Secrets vault requires an independent, non-reference bootstrap credential.", bootstrap ? [bootstrap.source] : []);
5743
+ const bootstrap2 = resolveCredential("secrets", secretsEnv);
5744
+ if (!bootstrap2 || bootstrap2.tier === "pointer") {
5745
+ throw new CredentialResolutionError("secrets", "The Secrets vault requires an independent, non-reference bootstrap credential.", bootstrap2 ? [bootstrap2.source] : []);
5746
5746
  }
5747
5747
  client = secretsSdk.createSecretsClientFromEnv(secretsEnv);
5748
5748
  } catch {
@@ -6203,11 +6203,11 @@ var init_db_writer = __esm(() => {
6203
6203
 
6204
6204
  // src/lib/hook-env.ts
6205
6205
  import { dirname as dirname24, isAbsolute as isAbsolute21 } from "path";
6206
- import { existsSync as existsSync14, realpathSync as realpathSync13, statSync as statSync2 } from "fs";
6206
+ import { existsSync as existsSync15, realpathSync as realpathSync13, statSync as statSync2 } from "fs";
6207
6207
  import { tmpdir as tmpdir2 } from "os";
6208
6208
  function isDeniedEnvName(name) {
6209
6209
  const upper = name.toUpperCase();
6210
- if (DENY_PREFIXES.some((prefix2) => upper.startsWith(prefix2)))
6210
+ if (DENY_PREFIXES.some((prefix3) => upper.startsWith(prefix3)))
6211
6211
  return true;
6212
6212
  if (DENY_SUFFIXES.some((suffix) => upper.endsWith(suffix)))
6213
6213
  return true;
@@ -6216,7 +6216,7 @@ function isDeniedEnvName(name) {
6216
6216
  function isInterpreterInjectionEnvName(name) {
6217
6217
  if (INTERPRETER_INJECTION.has(name))
6218
6218
  return true;
6219
- return INTERPRETER_INJECTION_PREFIXES.some((prefix2) => name.startsWith(prefix2));
6219
+ return INTERPRETER_INJECTION_PREFIXES.some((prefix3) => name.startsWith(prefix3));
6220
6220
  }
6221
6221
  function isBashFunctionEnvName(name) {
6222
6222
  return name.startsWith("BASH_FUNC_");
@@ -6251,7 +6251,7 @@ function isUnsafePathEntry(entry, home) {
6251
6251
  }
6252
6252
  function sanitizeHookPath(pathValue, home) {
6253
6253
  const baseline = PATH_BASELINE.slice();
6254
- if (process.platform === "darwin" && existsSync14("/opt/homebrew/bin"))
6254
+ if (process.platform === "darwin" && existsSync15("/opt/homebrew/bin"))
6255
6255
  baseline.push("/opt/homebrew/bin");
6256
6256
  const bunDir = dirname24(process.execPath);
6257
6257
  const seen = new Set;
@@ -6378,7 +6378,7 @@ __export(exports_run, {
6378
6378
  });
6379
6379
  import { randomBytes as randomBytes2 } from "crypto";
6380
6380
  import { rmSync as rmSync4, writeFileSync as writeFileSync13 } from "fs";
6381
- import { basename as basename11, dirname as dirname25, isAbsolute as isAbsolute23, join as join30 } from "path";
6381
+ import { basename as basename11, dirname as dirname25, isAbsolute as isAbsolute23, join as join29 } from "path";
6382
6382
  function extensionOf(scriptPath) {
6383
6383
  const base = basename11(scriptPath);
6384
6384
  const dot = base.lastIndexOf(".");
@@ -6449,7 +6449,7 @@ async function readPipeWithDeadline(stream, deadlineMs) {
6449
6449
  async function executeVerifiedScript(options) {
6450
6450
  const scriptDir = dirname25(options.scriptPath);
6451
6451
  const interpreter = interpreterFor(options.name, options.scriptPath, options.content);
6452
- const tempPath = join30(scriptDir, `.hooks-verified-${randomBytes2(12).toString("hex")}.${interpreter.tempExt}`);
6452
+ const tempPath = join29(scriptDir, `.hooks-verified-${randomBytes2(12).toString("hex")}.${interpreter.tempExt}`);
6453
6453
  let proc = null;
6454
6454
  let timedOut = false;
6455
6455
  const timeoutTimer = options.timeout ? setTimeout(() => {
@@ -7542,9 +7542,9 @@ function getHook(name) {
7542
7542
  return HOOKS.find((h) => h.name === name);
7543
7543
  }
7544
7544
  // src/lib/installer.ts
7545
- import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync7, readdirSync as readdirSync4, realpathSync as realpathSync5, rmdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "fs";
7545
+ import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync7, readdirSync as readdirSync4, realpathSync as realpathSync5, rmdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "fs";
7546
7546
  import { join as join10, dirname as dirname7, basename as basename3, resolve as resolve7 } from "path";
7547
- import { homedir as homedir5 } from "os";
7547
+ import { homedir as homedir6 } from "os";
7548
7548
  import { fileURLToPath as fileURLToPath2 } from "url";
7549
7549
 
7550
7550
  // src/lib/resolve.ts
@@ -7769,6 +7769,169 @@ function installedNativeSafetyCommand(name) {
7769
7769
  return buildNativeSafetyCommand({ name, runtime, runtimeSHA256: pinEligibleFile(runtime, 200000000, true), worker: canonicalWorker, workerSHA256: pinEligibleFile(canonicalWorker, 256000), home, path: `${dirname3(runtime)}:${join7(home, ".bun/bin")}:/usr/local/bin:/usr/bin:/bin` });
7770
7770
  }
7771
7771
 
7772
+ // src/lib/native-readiness-command.ts
7773
+ import { createHash as createHash2 } from "crypto";
7774
+ import { constants as constants2, closeSync as closeSync2, existsSync as existsSync7, fstatSync as fstatSync2, lstatSync as lstatSync2, openSync as openSync2, readFileSync as readFileSync4, realpathSync as realpathSync3 } from "fs";
7775
+ import { dirname as dirname4, isAbsolute as isAbsolute3, join as join8 } from "path";
7776
+ import { homedir as homedir4 } from "os";
7777
+
7778
+ // src/lib/readiness-process.ts
7779
+ var supervisor = String.raw`
7780
+ const {spawn}=require("node:child_process");
7781
+ const spec=JSON.parse(process.argv.at(-1));
7782
+ // This runtime was created detached and is its group's live leader. Its
7783
+ // independent timer and parent pipe remain effective if the observer exits
7784
+ // or its event loop stops responding. Only this freshly created tree is hit.
7785
+ const cleanup=()=>{try{process.kill(-process.pid,"SIGKILL")}catch{process.exit(1)}};
7786
+ setTimeout(cleanup,spec.timeout);
7787
+ process.stdin.once("end",cleanup);
7788
+ process.stdin.once("error",cleanup);
7789
+ process.stdin.on("data",cleanup);
7790
+ process.stdin.resume();
7791
+ for(const signal of ["SIGTERM","SIGINT","SIGHUP"])process.once(signal,cleanup);
7792
+ let done=false, out=Buffer.alloc(0), err=Buffer.alloc(0);
7793
+ function report(status,error){
7794
+ if(done)return; done=true;
7795
+ process.stdout.write(JSON.stringify({status,error,stdout:out.toString("utf8"),stderr:err.toString("utf8")})+"\n");
7796
+ }
7797
+ const child=spawn(spec.command,spec.args,{stdio:["ignore","pipe","pipe"],env:process.env});
7798
+ for(const [stream,name] of [[child.stdout,"stdout"],[child.stderr,"stderr"]]){
7799
+ stream.on("data",bytes=>{
7800
+ if(done)return;
7801
+ const before=name==="stdout"?out:err;
7802
+ if(before.length+bytes.length>spec.maxBuffer){report(null,{code:"ENOBUFS"});return}
7803
+ if(name==="stdout")out=Buffer.concat([out,bytes]);else err=Buffer.concat([err,bytes]);
7804
+ });
7805
+ stream.on("error",()=>report(null,{code:"ECHILD"}));
7806
+ }
7807
+ child.on("error",()=>report(null,{code:"ECHILD"}));
7808
+ // A successful observation includes complete streams. A nested client that
7809
+ // keeps them open after its parent exits is a failure requiring group cleanup.
7810
+ child.on("exit",()=>setTimeout(()=>report(null,{code:"ECHILD"}),100));
7811
+ child.on("close",(status,signal)=>report(status,signal?{code:"ECHILD"}:undefined));
7812
+ `;
7813
+
7814
+ // src/native-readiness.ts
7815
+ function isReadinessProvider(value) {
7816
+ return value === "claude" || value === "codex" || value === "sumi";
7817
+ }
7818
+ var safeFailureCodes = new Set([
7819
+ "unsupported_platform",
7820
+ "station_detection_failed",
7821
+ "station_mismatch",
7822
+ "hosted_unavailable",
7823
+ "authentication_refused",
7824
+ "station_setup_required",
7825
+ "invalid_status",
7826
+ "deadline_exceeded",
7827
+ "retention_mismatch",
7828
+ "managed_instructions_refused",
7829
+ "removal_instruction_missing",
7830
+ "instructions_changed",
7831
+ "native_config_changed",
7832
+ "guard_refused",
7833
+ "owner_refused"
7834
+ ]);
7835
+
7836
+ // src/lib/native-readiness-command.ts
7837
+ var quote2 = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
7838
+ var bootstrap = String.raw`/* hasna-native-readiness-v1 */ (() => {
7839
+ const fs = require("node:fs"), path = require("node:path"), crypto = require("node:crypto");
7840
+ const failed = () => process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:"Trash readiness needs attention (runner integrity). Preserve files; do not fall back to direct deletion."}})+"\n");
7841
+ try {
7842
+ const b = JSON.parse(Buffer.from(process.argv[1],"base64url").toString("utf8"));
7843
+ if (!["claude","codex","sumi"].includes(b.provider) || process.argv[2] !== b.provider || process.env.HOME !== b.home) throw Error();
7844
+ const check = (file, expected, maximum, executable) => {
7845
+ if (!path.isAbsolute(file) || fs.realpathSync(file) !== file) throw Error();
7846
+ for (let p = path.dirname(file);; p = path.dirname(p)) {
7847
+ const s = fs.lstatSync(p);
7848
+ if (!s.isDirectory() || ![0,process.getuid()].includes(s.uid) || s.mode & 0o022) throw Error();
7849
+ if (p === path.dirname(p)) break;
7850
+ }
7851
+ const fd = fs.openSync(file,fs.constants.O_RDONLY|fs.constants.O_NOFOLLOW|fs.constants.O_NONBLOCK);
7852
+ try {
7853
+ const s = fs.fstatSync(fd);
7854
+ if (!s.isFile() || ![0,process.getuid()].includes(s.uid) || s.mode & 0o022 || s.size > maximum || executable && !(s.mode & 0o100)) throw Error();
7855
+ const bytes = Buffer.alloc(s.size+1); let length=0;
7856
+ while (length<bytes.length) { const n=fs.readSync(fd,bytes,length,bytes.length-length,null); if(!n)break; length+=n; }
7857
+ const after=fs.fstatSync(fd);
7858
+ if(length!==s.size || after.size!==s.size || after.mtimeMs!==s.mtimeMs || after.ctimeMs!==s.ctimeMs)throw Error();
7859
+ const content=bytes.subarray(0,length);
7860
+ if(crypto.createHash("sha256").update(content).digest("hex")!==expected)throw Error();
7861
+ return content;
7862
+ } finally { fs.closeSync(fd); }
7863
+ };
7864
+ check(b.runtime,b.runtimeSHA256,200000000,true);
7865
+ const bytes=check(b.worker,b.workerSHA256,256000,false);
7866
+ new Function("require","process",bytes.toString("utf8"))(require,process);
7867
+ } catch { failed(); }
7868
+ })();`;
7869
+ var prefix2 = "/usr/bin/env -i ";
7870
+ function valid(b) {
7871
+ if (!b || Object.keys(b).sort().join() !== "home,provider,runtime,runtimeSHA256,worker,workerSHA256" || !isReadinessProvider(b.provider) || ![b.home, b.runtime, b.worker].every((p) => typeof p === "string" && isAbsolute3(p) && !/[\0\r\n]/.test(p)) || ![b.runtimeSHA256, b.workerSHA256].every((h) => typeof h === "string" && /^[a-f0-9]{64}$/.test(h)))
7872
+ throw new Error("Invalid readiness command binding");
7873
+ }
7874
+ function buildNativeReadinessCommand(binding) {
7875
+ valid(binding);
7876
+ const encoded = Buffer.from(JSON.stringify(binding)).toString("base64url");
7877
+ return prefix2 + ["HOME=" + binding.home, "PATH=/usr/bin:/bin", binding.runtime, "--config=/dev/null", "--no-env-file", "--no-macros", "--no-install", "--eval", bootstrap, encoded, binding.provider].map(quote2).join(" ");
7878
+ }
7879
+ function nativeReadinessCommandBinding(command) {
7880
+ if (!command.startsWith(prefix2) || !command.includes(quote2(bootstrap)))
7881
+ return;
7882
+ const tail = command.slice(command.indexOf(quote2(bootstrap)) + quote2(bootstrap).length);
7883
+ const match = /^ '([A-Za-z0-9_-]{1,16384})' '(claude|codex|sumi)'$/.exec(tail);
7884
+ if (!match)
7885
+ return;
7886
+ try {
7887
+ const b = JSON.parse(Buffer.from(match[1], "base64url").toString("utf8"));
7888
+ return buildNativeReadinessCommand(b) === command ? b : undefined;
7889
+ } catch {
7890
+ return;
7891
+ }
7892
+ }
7893
+ function pin(path, maximum, executable) {
7894
+ if (realpathSync3(path) !== path)
7895
+ throw new Error("Readiness runner requires canonical files");
7896
+ for (let p = dirname4(path);; p = dirname4(p)) {
7897
+ const s = lstatSync2(p);
7898
+ if (!s.isDirectory() || ![0, process.getuid?.()].includes(s.uid) || s.mode & 18)
7899
+ throw new Error("Readiness runner requires safe ancestry");
7900
+ if (p === dirname4(p))
7901
+ break;
7902
+ }
7903
+ const fd = openSync2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
7904
+ try {
7905
+ const s = fstatSync2(fd);
7906
+ if (!s.isFile() || ![0, process.getuid?.()].includes(s.uid) || s.mode & 18 || s.size > maximum || executable && !(s.mode & 64))
7907
+ throw new Error("Readiness runner requires safe regular files");
7908
+ const bytes = readFileSync4(fd);
7909
+ if (bytes.length > maximum)
7910
+ throw new Error("Readiness runner is too large");
7911
+ return createHash2("sha256").update(bytes).digest("hex");
7912
+ } finally {
7913
+ closeSync2(fd);
7914
+ }
7915
+ }
7916
+ function nativeReadinessCommandVerified(command) {
7917
+ const b = nativeReadinessCommandBinding(command);
7918
+ if (!b)
7919
+ return false;
7920
+ try {
7921
+ return pin(b.runtime, 200000000, true) === b.runtimeSHA256 && pin(b.worker, 256000, false) === b.workerSHA256;
7922
+ } catch {
7923
+ return false;
7924
+ }
7925
+ }
7926
+ function installedNativeReadinessCommand(provider) {
7927
+ const candidates = [join8(import.meta.dir, "../../bin/native-readiness-entry.js"), join8(import.meta.dir, "../bin/native-readiness-entry.js"), join8(import.meta.dir, "native-readiness-entry.js")];
7928
+ const selected = candidates.find(existsSync7);
7929
+ if (!selected)
7930
+ throw new Error("Build or install the native readiness runner first");
7931
+ const runtime = realpathSync3(process.execPath), worker = realpathSync3(selected);
7932
+ return buildNativeReadinessCommand({ provider, home: realpathSync3(homedir4()), runtime, runtimeSHA256: pin(runtime, 200000000, true), worker, workerSHA256: pin(worker, 256000, false) });
7933
+ }
7934
+
7772
7935
  // src/lib/mementos-options.ts
7773
7936
  function validateMementosRegistration(name, value) {
7774
7937
  if (value === undefined)
@@ -7833,6 +7996,8 @@ function registrationName(command) {
7833
7996
  const native = nativeSafetyRegistration(command);
7834
7997
  if (native)
7835
7998
  return native;
7999
+ if (nativeReadinessCommandBinding(command))
8000
+ return "trash-readiness";
7836
8001
  if (/^hooks run mementos-context(?:\s|$)/.test(command.trim())) {
7837
8002
  try {
7838
8003
  retainedMementosRegistration([command.trim()]);
@@ -7852,16 +8017,16 @@ function matchersOverlap(a, b) {
7852
8017
  init_store();
7853
8018
 
7854
8019
  // src/lib/codex-settings.ts
7855
- import { closeSync as closeSync2, existsSync as existsSync8, lstatSync as lstatSync2, mkdirSync as mkdirSync4, openSync as openSync2, readFileSync as readFileSync5, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
7856
- import { dirname as dirname4 } from "path";
7857
- import { createHash as createHash3, randomUUID } from "crypto";
8020
+ import { closeSync as closeSync3, existsSync as existsSync9, lstatSync as lstatSync3, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync6, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
8021
+ import { dirname as dirname5 } from "path";
8022
+ import { createHash as createHash4, randomUUID } from "crypto";
7858
8023
  var snapshots = new WeakMap;
7859
8024
  function current(path) {
7860
8025
  try {
7861
- const stat = lstatSync2(path);
8026
+ const stat = lstatSync3(path);
7862
8027
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1048576)
7863
8028
  throw new Error("invalid_file");
7864
- return readFileSync5(path, "utf8");
8029
+ return readFileSync6(path, "utf8");
7865
8030
  } catch (error) {
7866
8031
  if (error.code === "ENOENT")
7867
8032
  return null;
@@ -7898,7 +8063,7 @@ function capturedCodexSettings(settings) {
7898
8063
  function assertSettingsPreimage(raw, expected) {
7899
8064
  if (expected === undefined)
7900
8065
  return;
7901
- if (!/^(?:absent|[a-f0-9]{64})$/.test(expected) || (raw === null ? "absent" : createHash3("sha256").update(raw).digest("hex")) !== expected) {
8066
+ if (!/^(?:absent|[a-f0-9]{64})$/.test(expected) || (raw === null ? "absent" : createHash4("sha256").update(raw).digest("hex")) !== expected) {
7902
8067
  throw new Error("Hook settings changed since planning; no settings were written.");
7903
8068
  }
7904
8069
  }
@@ -7912,11 +8077,11 @@ function writeCodexSettings(path, settings, expectedSha256) {
7912
8077
  ` });
7913
8078
  }
7914
8079
  function writeHookSettingsPreimage(path, settings, before) {
7915
- mkdirSync4(dirname4(path), { recursive: true, mode: 448 });
8080
+ mkdirSync4(dirname5(path), { recursive: true, mode: 448 });
7916
8081
  const lock = `${path}.hooks-write.lock`;
7917
8082
  let fd;
7918
8083
  try {
7919
- fd = openSync2(lock, "wx", 384);
8084
+ fd = openSync3(lock, "wx", 384);
7920
8085
  } catch {
7921
8086
  throw new Error("Codex settings are being edited; retry after the active writer finishes.");
7922
8087
  }
@@ -7931,23 +8096,23 @@ function writeHookSettingsPreimage(path, settings, before) {
7931
8096
  throw new Error("Hook settings changed since reading; re-read before retrying.");
7932
8097
  renameSync2(temp, path);
7933
8098
  } finally {
7934
- if (existsSync8(temp))
8099
+ if (existsSync9(temp))
7935
8100
  unlinkSync(temp);
7936
- closeSync2(fd);
8101
+ closeSync3(fd);
7937
8102
  unlinkSync(lock);
7938
8103
  }
7939
8104
  }
7940
8105
 
7941
8106
  // src/lib/claude-skills-coordination.ts
7942
- import { createHash as createHash5 } from "crypto";
8107
+ import { createHash as createHash6 } from "crypto";
7943
8108
 
7944
8109
  // ../../node_modules/.bun/@hasna+skills@0.9.17+a00d3064559565bb/node_modules/@hasna/skills/dist/index.js
7945
- import { existsSync as existsSync9 } from "fs";
7946
- import { homedir as homedir4 } from "os";
7947
- import { join as join8, resolve as resolve3 } from "path";
8110
+ import { existsSync as existsSync10 } from "fs";
8111
+ import { homedir as homedir5 } from "os";
8112
+ import { join as join9, resolve as resolve3 } from "path";
7948
8113
  import { homedir as pathsResolverHomedir2 } from "os";
7949
8114
  import { join as pathsResolverJoin2 } from "path";
7950
- import { createHash as createHash4 } from "crypto";
8115
+ import { createHash as createHash5 } from "crypto";
7951
8116
  import { existsSync as existsSync52, readdirSync as readdirSync3, readFileSync as readFileSync42, statSync as statSync3 } from "fs";
7952
8117
  import { join as join52, sep as sep2 } from "path";
7953
8118
  import { basename as basename2, isAbsolute as isAbsolute22, resolve as resolve22 } from "path";
@@ -7955,23 +8120,23 @@ import { createHash as createHash16 } from "crypto";
7955
8120
  import { lstatSync as lstatSync20, readFileSync as readFileSync16, readdirSync as readdirSync9, realpathSync as realpathSync4 } from "fs";
7956
8121
  import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute14, join as join26, resolve as resolve14, sep as sep5 } from "path";
7957
8122
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
7958
- import { dirname as dirname32, isAbsolute as isAbsolute3, join as join72, resolve as resolve32 } from "path";
7959
- import { lstatSync as lstatSync3, readFileSync as readFileSync6, opendirSync } from "fs";
8123
+ import { dirname as dirname32, isAbsolute as isAbsolute32, join as join72, resolve as resolve32 } from "path";
8124
+ import { lstatSync as lstatSync32, readFileSync as readFileSync62, opendirSync } from "fs";
7960
8125
  import { createHash as createHash22 } from "crypto";
7961
8126
  import { lstatSync as lstatSync4, opendirSync as opendirSync2 } from "fs";
7962
8127
  import { dirname as dirname42, isAbsolute as isAbsolute4, join as join82, resolve as resolve4 } from "path";
7963
8128
  import { createHash as createHash32 } from "crypto";
7964
- import { closeSync as closeSync3, constants as constants2, fstatSync as fstatSync2, lstatSync as lstatSync5, openSync as openSync3, readSync } from "fs";
7965
- import { dirname as dirname5, isAbsolute as isAbsolute5, resolve as resolve5 } from "path";
8129
+ import { closeSync as closeSync4, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync5, openSync as openSync4, readSync } from "fs";
8130
+ import { dirname as dirname52, isAbsolute as isAbsolute5, resolve as resolve5 } from "path";
7966
8131
  import { createHash as createHash42 } from "crypto";
7967
8132
  import { lstatSync as lstatSync6, readlinkSync } from "fs";
7968
- import { dirname as dirname6, isAbsolute as isAbsolute6, join as join9, parse, resolve as resolve6, sep as sep22 } from "path";
8133
+ import { dirname as dirname6, isAbsolute as isAbsolute6, join as join92, parse, resolve as resolve6, sep as sep22 } from "path";
7969
8134
  import { closeSync as closeSync12, constants as constants9, fstatSync as fstatSync12, lstatSync as lstatSync17, openSync as openSync12, opendirSync as opendirSync5, readSync as readSync8 } from "fs";
7970
8135
  import { basename as basename6, dirname as dirname13, isAbsolute as isAbsolute12, join as join25, resolve as resolve12 } from "path";
7971
8136
  import { isAbsolute as isAbsolute11, join as join23, resolve as resolve11 } from "path";
7972
8137
  import { createRequire } from "module";
7973
8138
  import { createHash as createHash10, randomUUID as randomUUID2 } from "crypto";
7974
- import { existsSync as existsSync82, lstatSync as lstatSync14, statSync as statSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync11, readdirSync as readdirSync7, opendirSync as opendirSync3, readlinkSync as readlinkSync2, realpathSync as realpathSync3, renameSync as renameSync22, rmdirSync as rmdirSync2, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, chmodSync, openSync as openSync10, closeSync as closeSync10, fsyncSync as fsyncSync2, fstatSync as fstatSync10, readSync as readSync6, constants as constants7, linkSync } from "fs";
8139
+ import { existsSync as existsSync82, lstatSync as lstatSync14, statSync as statSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync11, readdirSync as readdirSync7, opendirSync as opendirSync3, readlinkSync as readlinkSync2, realpathSync as realpathSync32, renameSync as renameSync22, rmdirSync as rmdirSync2, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, chmodSync, openSync as openSync10, closeSync as closeSync10, fsyncSync as fsyncSync2, fstatSync as fstatSync10, readSync as readSync6, constants as constants7, linkSync } from "fs";
7975
8140
  import { dirname as dirname10, join as join16, resolve as resolve9, relative as relative4, isAbsolute as isAbsolute9, sep as sep4 } from "path";
7976
8141
  import { homedir as homedir22 } from "os";
7977
8142
  import { closeSync as closeSync8, constants as constants5, fstatSync as fstatSync8, lstatSync as lstatSync11, openSync as openSync8, readSync as readSync4 } from "fs";
@@ -8272,8 +8437,8 @@ var require_directives = __commonJS((exports) => {
8272
8437
  if (parts.length < 2)
8273
8438
  return false;
8274
8439
  }
8275
- const [handle, prefix2] = parts;
8276
- this.tags[handle] = prefix2;
8440
+ const [handle, prefix3] = parts;
8441
+ this.tags[handle] = prefix3;
8277
8442
  return true;
8278
8443
  }
8279
8444
  case "%YAML": {
@@ -8317,10 +8482,10 @@ var require_directives = __commonJS((exports) => {
8317
8482
  const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s);
8318
8483
  if (!suffix)
8319
8484
  onError(`The ${source} tag has no suffix`);
8320
- const prefix2 = this.tags[handle];
8321
- if (prefix2) {
8485
+ const prefix3 = this.tags[handle];
8486
+ if (prefix3) {
8322
8487
  try {
8323
- return prefix2 + decodeURIComponent(suffix);
8488
+ return prefix3 + decodeURIComponent(suffix);
8324
8489
  } catch (error) {
8325
8490
  onError(String(error));
8326
8491
  return null;
@@ -8332,9 +8497,9 @@ var require_directives = __commonJS((exports) => {
8332
8497
  return null;
8333
8498
  }
8334
8499
  tagString(tag) {
8335
- for (const [handle, prefix2] of Object.entries(this.tags)) {
8336
- if (tag.startsWith(prefix2))
8337
- return handle + escapeTagName(tag.substring(prefix2.length));
8500
+ for (const [handle, prefix3] of Object.entries(this.tags)) {
8501
+ if (tag.startsWith(prefix3))
8502
+ return handle + escapeTagName(tag.substring(prefix3.length));
8338
8503
  }
8339
8504
  return tag[0] === "!" ? tag : `!<${tag}>`;
8340
8505
  }
@@ -8351,11 +8516,11 @@ var require_directives = __commonJS((exports) => {
8351
8516
  tagNames = Object.keys(tags);
8352
8517
  } else
8353
8518
  tagNames = [];
8354
- for (const [handle, prefix2] of tagEntries) {
8355
- if (handle === "!!" && prefix2 === "tag:yaml.org,2002:")
8519
+ for (const [handle, prefix3] of tagEntries) {
8520
+ if (handle === "!!" && prefix3 === "tag:yaml.org,2002:")
8356
8521
  continue;
8357
- if (!doc || tagNames.some((tn) => tn.startsWith(prefix2)))
8358
- lines.push(`%TAG ${handle} ${prefix2}`);
8522
+ if (!doc || tagNames.some((tn) => tn.startsWith(prefix3)))
8523
+ lines.push(`%TAG ${handle} ${prefix3}`);
8359
8524
  }
8360
8525
  return lines.join(`
8361
8526
  `);
@@ -8386,14 +8551,14 @@ var require_anchors = __commonJS((exports) => {
8386
8551
  });
8387
8552
  return anchors;
8388
8553
  }
8389
- function findNewAnchor(prefix2, exclude) {
8554
+ function findNewAnchor(prefix3, exclude) {
8390
8555
  for (let i = 1;; ++i) {
8391
- const name = `${prefix2}${i}`;
8556
+ const name = `${prefix3}${i}`;
8392
8557
  if (!exclude.has(name))
8393
8558
  return name;
8394
8559
  }
8395
8560
  }
8396
- function createNodeAnchors(doc, prefix2) {
8561
+ function createNodeAnchors(doc, prefix3) {
8397
8562
  const aliasObjects = [];
8398
8563
  const sourceObjects = new Map;
8399
8564
  let prevAnchors = null;
@@ -8401,7 +8566,7 @@ var require_anchors = __commonJS((exports) => {
8401
8566
  onAnchor: (source) => {
8402
8567
  aliasObjects.push(source);
8403
8568
  prevAnchors ?? (prevAnchors = anchorNames(doc));
8404
- const anchor = findNewAnchor(prefix2, prevAnchors);
8569
+ const anchor = findNewAnchor(prefix3, prevAnchors);
8405
8570
  prevAnchors.add(anchor);
8406
8571
  return anchor;
8407
8572
  },
@@ -10226,10 +10391,10 @@ var require_int = __commonJS((exports) => {
10226
10391
  var stringifyNumber = require_stringifyNumber();
10227
10392
  var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value);
10228
10393
  var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix);
10229
- function intStringify(node, radix, prefix2) {
10394
+ function intStringify(node, radix, prefix3) {
10230
10395
  const { value } = node;
10231
10396
  if (intIdentify(value) && value >= 0)
10232
- return prefix2 + value.toString(radix);
10397
+ return prefix3 + value.toString(radix);
10233
10398
  return stringifyNumber.stringifyNumber(node);
10234
10399
  }
10235
10400
  var intOct = {
@@ -10640,11 +10805,11 @@ var require_int2 = __commonJS((exports) => {
10640
10805
  const n = parseInt(str, radix);
10641
10806
  return sign === "-" ? -1 * n : n;
10642
10807
  }
10643
- function intStringify(node, radix, prefix2) {
10808
+ function intStringify(node, radix, prefix3) {
10644
10809
  const { value } = node;
10645
10810
  if (intIdentify(value)) {
10646
10811
  const str = value.toString(radix);
10647
- return value < 0 ? "-" + prefix2 + str.substr(1) : prefix2 + str;
10812
+ return value < 0 ? "-" + prefix3 + str.substr(1) : prefix3 + str;
10648
10813
  }
10649
10814
  return stringifyNumber.stringifyNumber(node);
10650
10815
  }
@@ -13558,9 +13723,9 @@ var require_lexer = __commonJS((exports) => {
13558
13723
  }
13559
13724
  }
13560
13725
  *parseQuotedScalar() {
13561
- const quote2 = this.charAt(0);
13562
- let end = this.buffer.indexOf(quote2, this.pos + 1);
13563
- if (quote2 === "'") {
13726
+ const quote3 = this.charAt(0);
13727
+ let end = this.buffer.indexOf(quote3, this.pos + 1);
13728
+ if (quote3 === "'") {
13564
13729
  while (end !== -1 && this.buffer[end + 1] === "'")
13565
13730
  end = this.buffer.indexOf("'", end + 2);
13566
13731
  } else {
@@ -14845,10 +15010,10 @@ var SKILLS_HOME_ENV = "SKILLS_HOME";
14845
15010
  var DEFAULT_SQLITE_FILENAME = "server.db";
14846
15011
  var GLOBAL_CONFIG_FILENAME = "config.json";
14847
15012
  function effectiveHome() {
14848
- return process.env["HOME"] || process.env["USERPROFILE"] || homedir4() || "/tmp";
15013
+ return process.env["HOME"] || process.env["USERPROFILE"] || homedir5() || "/tmp";
14849
15014
  }
14850
15015
  function legacyDataRoot() {
14851
- return join8(effectiveHome(), ".hasna", "skills");
15016
+ return join9(effectiveHome(), ".hasna", "skills");
14852
15017
  }
14853
15018
  function resolverDataRoot(home = effectiveHome(), env) {
14854
15019
  return dataDir2({ app: "skills", home, env });
@@ -14857,7 +15022,7 @@ function adoptResolverDataRoot2(resolved, env = process.env) {
14857
15022
  const dataOverride = env.HASNA_DATA_HOME;
14858
15023
  if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
14859
15024
  return true;
14860
- return existsSync9(join8(resolved, DEFAULT_SQLITE_FILENAME)) || existsSync9(join8(resolved, GLOBAL_CONFIG_FILENAME));
15025
+ return existsSync10(join9(resolved, DEFAULT_SQLITE_FILENAME)) || existsSync10(join9(resolved, GLOBAL_CONFIG_FILENAME));
14861
15026
  }
14862
15027
  function exactDataRoot() {
14863
15028
  for (const key of [DATA_DIR_ENV, HASNA_SKILLS_HOME_ENV, SKILLS_HOME_ENV]) {
@@ -15091,7 +15256,7 @@ function* bundleHashParts(files) {
15091
15256
  yield new TextEncoder().encode("\x00");
15092
15257
  }
15093
15258
  function hashBundleFiles(files) {
15094
- const hash = createHash4(CONTENT_HASH_ALGORITHM);
15259
+ const hash = createHash5(CONTENT_HASH_ALGORITHM);
15095
15260
  for (const part of bundleHashParts(files))
15096
15261
  hash.update(part);
15097
15262
  return hash.digest("hex");
@@ -15546,10 +15711,10 @@ function quoted(value) {
15546
15711
  throw new Error("Invalid Hermes Skills command");
15547
15712
  return `'${value.replace(/'/g, `'\\''`)}'`;
15548
15713
  }
15549
- function hermesHookDefinitions(supervisor) {
15550
- if (!isAbsolute3(supervisor.runtime) || !isAbsolute3(supervisor.path))
15714
+ function hermesHookDefinitions(supervisor2) {
15715
+ if (!isAbsolute32(supervisor2.runtime) || !isAbsolute32(supervisor2.path))
15551
15716
  throw new Error("Expected absolute Hermes supervisor paths");
15552
- const entry = (event) => ({ command: `${quoted(supervisor.runtime)} ${quoted(supervisor.path)} --event ${event}`, timeout: 15, ...event === "pre_tool_call" ? { fail_closed: true } : {} });
15717
+ const entry = (event) => ({ command: `${quoted(supervisor2.runtime)} ${quoted(supervisor2.path)} --event ${event}`, timeout: 15, ...event === "pre_tool_call" ? { fail_closed: true } : {} });
15553
15718
  return { pre_llm_call: entry("pre_llm_call"), pre_tool_call: entry("pre_tool_call") };
15554
15719
  }
15555
15720
  function assertHermesEnvironment(home) {
@@ -15560,30 +15725,30 @@ function assertHermesEnvironment(home) {
15560
15725
  throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
15561
15726
  }
15562
15727
  const selected = process.env.HERMES_HOME?.trim();
15563
- if (selected && (!isAbsolute3(selected) || resolve32(selected) !== join72(resolve32(home), ".hermes")))
15728
+ if (selected && (!isAbsolute32(selected) || resolve32(selected) !== join72(resolve32(home), ".hermes")))
15564
15729
  throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
15565
15730
  if (process.env.HERMES_ENABLE_PROJECT_PLUGINS?.trim() && !["0", "false", "no", "off"].includes(process.env.HERMES_ENABLE_PROJECT_PLUGINS.trim().toLowerCase()))
15566
15731
  throw new Error("NATIVE_SKILL_DRIFT: Hermes project plugins require a dedicated discovery review");
15567
15732
  }
15568
15733
  function read(path) {
15569
15734
  for (let current2 = path;; current2 = dirname32(current2)) {
15570
- if (lstatSync3(current2, { throwIfNoEntry: false })?.isSymbolicLink())
15735
+ if (lstatSync32(current2, { throwIfNoEntry: false })?.isSymbolicLink())
15571
15736
  throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes protection input");
15572
15737
  if (dirname32(current2) === current2)
15573
15738
  break;
15574
15739
  }
15575
- const stat = lstatSync3(path, { throwIfNoEntry: false });
15740
+ const stat = lstatSync32(path, { throwIfNoEntry: false });
15576
15741
  if (!stat)
15577
15742
  return null;
15578
15743
  if (!stat.isFile() || stat.size > 1024 * 1024)
15579
15744
  throw new Error("NATIVE_SKILL_DRIFT: invalid Hermes protection input");
15580
- return readFileSync6(path, "utf8");
15745
+ return readFileSync62(path, "utf8");
15581
15746
  }
15582
- function assertHermesProtection(home, config, command, profile, supervisor) {
15747
+ function assertHermesProtection(home, config, command, profile, supervisor2) {
15583
15748
  assertHermesEnvironment(home);
15584
15749
  if (read(join72(home, ".hermes", HERMES_OPT_OUT)) === null)
15585
15750
  throw new Error("NATIVE_SKILL_DRIFT: Hermes bundled skill reseeding is not disabled; run skills hook install");
15586
- if (!supervisor || !isAbsolute3(supervisor.path) || supervisor.runtime !== process.execPath || read(supervisor.path) !== renderHermesSupervisor(command, profile))
15751
+ if (!supervisor2 || !isAbsolute32(supervisor2.path) || supervisor2.runtime !== process.execPath || read(supervisor2.path) !== renderHermesSupervisor(command, profile))
15587
15752
  throw new Error("NATIVE_SKILL_DRIFT: Hermes owned supervisor changed; run skills hook install");
15588
15753
  const raw = read(join72(home, ".hermes/shell-hooks-allowlist.json"));
15589
15754
  let trust;
@@ -15592,7 +15757,7 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
15592
15757
  } catch {
15593
15758
  throw new Error("NATIVE_SKILL_DRIFT: invalid Hermes native hook trust");
15594
15759
  }
15595
- for (const [event, entry] of Object.entries(hermesHookDefinitions(supervisor))) {
15760
+ for (const [event, entry] of Object.entries(hermesHookDefinitions(supervisor2))) {
15596
15761
  if (!Array.isArray(config.hooks?.[event]) || config.hooks[event].filter((actual) => sameEntry(actual, entry)).length !== 1)
15597
15762
  throw new Error("NATIVE_SKILL_DRIFT: Hermes required native hooks changed; run skills hook install");
15598
15763
  if (!Array.isArray(trust?.approvals) || !trust.approvals.some((approval) => object2(approval) && approval.event === event && approval.command === entry.command))
@@ -15610,7 +15775,7 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
15610
15775
  function assertNoHermesLegacyShadow(root) {
15611
15776
  let entries = 0;
15612
15777
  for (let cursor = resolve32(root);; cursor = dirname32(cursor)) {
15613
- if (lstatSync3(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
15778
+ if (lstatSync32(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
15614
15779
  throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes legacy discovery root");
15615
15780
  if (dirname32(cursor) === cursor)
15616
15781
  break;
@@ -15618,7 +15783,7 @@ function assertNoHermesLegacyShadow(root) {
15618
15783
  function scan(path, depth) {
15619
15784
  if (++entries > 1e4 || depth > 64)
15620
15785
  throw new Error("NATIVE_SKILL_DRIFT: Hermes legacy discovery limit exceeded; review the root before continuing");
15621
- const stat = lstatSync3(path, { throwIfNoEntry: false });
15786
+ const stat = lstatSync32(path, { throwIfNoEntry: false });
15622
15787
  if (!stat)
15623
15788
  return;
15624
15789
  if (stat.isSymbolicLink())
@@ -15628,9 +15793,9 @@ function assertNoHermesLegacyShadow(root) {
15628
15793
  throw new Error("NATIVE_SKILL_DRIFT: unsupported Hermes legacy discovery input");
15629
15794
  return;
15630
15795
  }
15631
- if (lstatSync3(join72(path, "skills-cli.md"), { throwIfNoEntry: false }))
15796
+ if (lstatSync32(join72(path, "skills-cli.md"), { throwIfNoEntry: false }))
15632
15797
  throw new Error("NATIVE_SKILL_DRIFT: legacy Hermes skills-cli.md can shadow the owned bridge; preserve and retire it before continuing");
15633
- const hasDescriptor = Boolean(lstatSync3(join72(path, "SKILL.md"), { throwIfNoEntry: false }));
15798
+ const hasDescriptor = Boolean(lstatSync32(join72(path, "SKILL.md"), { throwIfNoEntry: false }));
15634
15799
  const directory = opendirSync(path);
15635
15800
  try {
15636
15801
  for (let child = directory.readSync();child; child = directory.readSync()) {
@@ -15763,10 +15928,10 @@ function discoveryByteBudget() {
15763
15928
  function safe2(path) {
15764
15929
  if (typeof path !== "string" || !isAbsolute5(path) || path !== resolve5(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
15765
15930
  throw new Error("Expected a canonical absolute raw discovery path");
15766
- for (let cursor = path;; cursor = dirname5(cursor)) {
15931
+ for (let cursor = path;; cursor = dirname52(cursor)) {
15767
15932
  if (lstatSync5(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
15768
15933
  throw new Error("Refusing symlink raw discovery input");
15769
- if (dirname5(cursor) === cursor)
15934
+ if (dirname52(cursor) === cursor)
15770
15935
  break;
15771
15936
  }
15772
15937
  }
@@ -15794,9 +15959,9 @@ function hashRawDiscoveryFile(path, budget, changes) {
15794
15959
  throw new Error("Unsupported or oversized raw discovery input");
15795
15960
  if (initial.size > budget.remaining)
15796
15961
  throw new Error("Raw discovery aggregate byte limit exceeded");
15797
- const fd = openSync3(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
15962
+ const fd = openSync4(path, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
15798
15963
  try {
15799
- const opened = fstatSync2(fd);
15964
+ const opened = fstatSync3(fd);
15800
15965
  if (!opened.isFile() || !same2(initial, opened))
15801
15966
  throw new Error("Raw discovery input changed during open");
15802
15967
  const hash = createHash32("sha256"), buffer = Buffer.alloc(64 * 1024);
@@ -15811,13 +15976,13 @@ function hashRawDiscoveryFile(path, budget, changes) {
15811
15976
  consume(budget, count);
15812
15977
  hash.update(buffer.subarray(0, count));
15813
15978
  }
15814
- const final = fstatSync2(fd), current2 = lstatSync5(path, { throwIfNoEntry: false });
15979
+ const final = fstatSync3(fd), current2 = lstatSync5(path, { throwIfNoEntry: false });
15815
15980
  safe2(path);
15816
15981
  if (!current2?.isFile() || !same2(opened, final) || !same2(final, current2) || bytes !== final.size)
15817
15982
  throw new Error("Raw discovery input changed during read");
15818
15983
  return hash.digest("hex");
15819
15984
  } finally {
15820
- closeSync3(fd);
15985
+ closeSync4(fd);
15821
15986
  }
15822
15987
  }
15823
15988
  function identity2(stat, mutable = false) {
@@ -15863,7 +16028,7 @@ function trace(path) {
15863
16028
  cursor = dirname6(cursor);
15864
16029
  continue;
15865
16030
  }
15866
- const candidate = join9(cursor, component);
16031
+ const candidate = join92(cursor, component);
15867
16032
  if (candidate.length > AGENT_POLICY_LIMITS.pathCharacters)
15868
16033
  throw new Error("Discovery resolved path length limit exceeded");
15869
16034
  const stat = lstatSync6(candidate, { bigint: true, throwIfNoEntry: false });
@@ -16873,7 +17038,7 @@ function assertSafePath(path) {
16873
17038
  }
16874
17039
  function readRootAlias(home, agent) {
16875
17040
  assertSafePath(home);
16876
- const realHome = realpathSync3(home), alias = join16(realHome, `.${agent}`);
17041
+ const realHome = realpathSync32(home), alias = join16(realHome, `.${agent}`);
16877
17042
  const stat = lstatSync14(alias, { throwIfNoEntry: false, bigint: true });
16878
17043
  if (!stat?.isSymbolicLink())
16879
17044
  throw new Error(`Agent root alias changed after planning: ${alias}`);
@@ -16882,7 +17047,7 @@ function readRootAlias(home, agent) {
16882
17047
  const targetStat = lstatSync14(targetPath, { throwIfNoEntry: false });
16883
17048
  if (!targetStat?.isDirectory())
16884
17049
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
16885
- const target = realpathSync3(targetPath), inside = relative4(realHome, target);
17050
+ const target = realpathSync32(targetPath), inside = relative4(realHome, target);
16886
17051
  if (!inside || inside === ".." || inside.startsWith(`..${sep4}`) || isAbsolute9(inside))
16887
17052
  throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
16888
17053
  return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
@@ -17416,10 +17581,10 @@ function assertManagedAgentBridge(agent, options = {}) {
17416
17581
  if (options.profileId !== undefined && options.profileId !== profile)
17417
17582
  throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
17418
17583
  if (agent === "hermes") {
17419
- const supervisor = binding.supervisors?.hermes;
17420
- if (supervisor?.path !== join16(dataDir22, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha(renderHermesSupervisor(command, profile)))
17584
+ const supervisor2 = binding.supervisors?.hermes;
17585
+ if (supervisor2?.path !== join16(dataDir22, "agent-hooks", "hermes.js") || supervisor2?.sha256 !== sha(renderHermesSupervisor(command, profile)))
17421
17586
  throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
17422
- assertHermesProtection(home, config, command, profile, supervisor);
17587
+ assertHermesProtection(home, config, command, profile, supervisor2);
17423
17588
  } else if (agent === "opencode") {
17424
17589
  if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(join16(home, ".config", "opencode", "plugins", "skills-cli.js")) !== renderOpenCodePlugin(command, profile))
17425
17590
  throw new Error("NATIVE_SKILL_DRIFT: OpenCode bridge protection changed; run skills hook install");
@@ -17711,9 +17876,9 @@ function snapshotPluginTree(root, options = {}) {
17711
17876
  const pins = directoryNames(absolute);
17712
17877
  pluginNeed(pins.length <= 1024, "Native plugin pin count exceeds its limit");
17713
17878
  directories.push({ path: absolute, stat: entryStat, names: pins });
17714
- for (const pin of pins) {
17715
- pluginNeed(/^[1-9][0-9]{0,9}$/.test(pin) && Number(pin) <= 2147483647, "Unrecognized native plugin pin name");
17716
- const pinPath = join21(absolute, pin), pinStat = stat(pinPath);
17879
+ for (const pin2 of pins) {
17880
+ pluginNeed(/^[1-9][0-9]{0,9}$/.test(pin2) && Number(pin2) <= 2147483647, "Unrecognized native plugin pin name");
17881
+ const pinPath = join21(absolute, pin2), pinStat = stat(pinPath);
17717
17882
  pluginNeed(pinStat, "Native plugin pin disappeared");
17718
17883
  const bytes2 = readBytes(pinPath, 4096, "native-pin");
17719
17884
  let value;
@@ -17723,7 +17888,7 @@ function snapshotPluginTree(root, options = {}) {
17723
17888
  pluginRefusal("Malformed native plugin process pin");
17724
17889
  }
17725
17890
  pluginKeys(value, ["pid", "procStart", "procStartFt"]);
17726
- pluginNeed(value.pid === Number(pin), "Native plugin pin identity differs from its name");
17891
+ pluginNeed(value.pid === Number(pin2), "Native plugin pin identity differs from its name");
17727
17892
  for (const key of ["procStart", "procStartFt"])
17728
17893
  pluginNeed(value[key] === undefined || typeof value[key] === "string" && value[key].length <= 128 && !/[\x00-\x1f\x7f]/.test(value[key]), "Invalid native plugin process-start witness");
17729
17894
  same3(pinStat, stat(pinPath));
@@ -17931,10 +18096,10 @@ function validatePlan(value) {
17931
18096
  pluginNeed(observedEvidenceDigest(plan.observation) === plan.evidenceDigest, "Plugin receipt observed evidence changed");
17932
18097
  pluginNeed(identityDigest(plan) === plan.planDigest, "Plugin receipt immutable identity changed");
17933
18098
  }
17934
- var quote2 = (value) => `'${value.replaceAll("'", "'\\''")}'`;
18099
+ var quote3 = (value) => `'${value.replaceAll("'", "'\\''")}'`;
17935
18100
  var resolverGuard = 'exec 9<"$1"; actual=$(/usr/bin/sha256sum /proc/self/fd/9); actual=${actual%% *}; [ "sha256:$actual" = "$2" ]; exec /proc/self/fd/9 integration plugin resolve --binding "$3"';
17936
18101
  function pluginResolverCommand(binding) {
17937
- return `${quote2("/bin/sh")} -eu -c ${quote2(resolverGuard)} plugin-resolver ${quote2(binding.target.resolver.executable)} ${quote2(binding.target.resolver.digest)} ${quote2(bindingId(binding))}`;
18102
+ return `${quote3("/bin/sh")} -eu -c ${quote3(resolverGuard)} plugin-resolver ${quote3(binding.target.resolver.executable)} ${quote3(binding.target.resolver.digest)} ${quote3(bindingId(binding))}`;
17938
18103
  }
17939
18104
  function pluginBindingPath(storeRoot, id) {
17940
18105
  pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
@@ -21661,11 +21826,11 @@ function isValidCIDRv6(value) {
21661
21826
  const parts = value.split("/");
21662
21827
  if (parts.length !== 2)
21663
21828
  return false;
21664
- const [address, prefix2] = parts;
21665
- if (!prefix2)
21829
+ const [address, prefix3] = parts;
21830
+ if (!prefix3)
21666
21831
  return false;
21667
- const prefixNum = Number(prefix2);
21668
- if (`${prefixNum}` !== prefix2)
21832
+ const prefixNum = Number(prefix3);
21833
+ if (`${prefixNum}` !== prefix3)
21669
21834
  return false;
21670
21835
  if (prefixNum < 0 || prefixNum > 128)
21671
21836
  return false;
@@ -31547,8 +31712,8 @@ function generateStringFormatCheck(doc, ctx, def, accessor) {
31547
31712
  doc.write(`if (!${accessor}.includes(${esc(def.includes)})) return INVALID;`);
31548
31713
  break;
31549
31714
  case "starts_with": {
31550
- const prefix2 = def.prefix;
31551
- doc.write(`if (${accessor}.slice(0, ${prefix2.length}) !== ${esc(prefix2)}) return INVALID;`);
31715
+ const prefix3 = def.prefix;
31716
+ doc.write(`if (${accessor}.slice(0, ${prefix3.length}) !== ${esc(prefix3)}) return INVALID;`);
31552
31717
  break;
31553
31718
  }
31554
31719
  case "ends_with": {
@@ -32281,8 +32446,8 @@ function generateDiscriminatedUnionCheck(doc, ctx, def, accessor) {
32281
32446
  claimed.add(value);
32282
32447
  }
32283
32448
  const conditions = Array.from(values, (value) => literalEquality(ctx, discVar, value));
32284
- const prefix2 = firstBranch ? "if" : "else if";
32285
- doc.write(`${prefix2} (${conditions.join(" || ")}) {`);
32449
+ const prefix3 = firstBranch ? "if" : "else if";
32450
+ doc.write(`${prefix3} (${conditions.join(" || ")}) {`);
32286
32451
  doc.indented((d) => {
32287
32452
  const branchOutput = generateCheck(d, ctx, option, accessor);
32288
32453
  d.write(`${outputVar} = ${branchOutput};`);
@@ -33136,12 +33301,12 @@ function _includes(includes, params) {
33136
33301
  includes
33137
33302
  });
33138
33303
  }
33139
- function _startsWith(prefix2, params) {
33304
+ function _startsWith(prefix3, params) {
33140
33305
  return new $ZodCheckStartsWith({
33141
33306
  check: "string_format",
33142
33307
  format: "starts_with",
33143
33308
  ...normalizeParams2(params),
33144
- prefix: prefix2
33309
+ prefix: prefix3
33145
33310
  });
33146
33311
  }
33147
33312
  function _endsWith(suffix, params) {
@@ -39109,7 +39274,7 @@ function coordinateClaudeSettings(before, replacement, options) {
39109
39274
  const plan = planClaudeHookEventsUpdate({
39110
39275
  ...options,
39111
39276
  events: selected,
39112
- expectedSettingsSha256: before === null ? "absent" : createHash5("sha256").update(before).digest("hex"),
39277
+ expectedSettingsSha256: before === null ? "absent" : createHash6("sha256").update(before).digest("hex"),
39113
39278
  replacement
39114
39279
  });
39115
39280
  if (!plan)
@@ -39120,7 +39285,7 @@ function coordinateClaudeSettings(before, replacement, options) {
39120
39285
 
39121
39286
  // src/lib/installer.ts
39122
39287
  var __dirname2 = dirname7(fileURLToPath2(import.meta.url));
39123
- var HOOKS_DIR = existsSync10(join10(__dirname2, "..", "..", "hooks", "hook-gitguard")) ? join10(__dirname2, "..", "..", "hooks") : join10(__dirname2, "..", "hooks");
39288
+ var HOOKS_DIR = existsSync11(join10(__dirname2, "..", "..", "hooks", "hook-gitguard")) ? join10(__dirname2, "..", "..", "hooks") : join10(__dirname2, "..", "hooks");
39124
39289
  function normalizeHookName(name) {
39125
39290
  return name.startsWith("hook-") ? name : `hook-${name}`;
39126
39291
  }
@@ -39208,11 +39373,11 @@ function getSettingsPath(scope = "global", target = "claude", codewithConfigPath
39208
39373
  return globalOverride;
39209
39374
  const dir = getTargetSettingsDir(target);
39210
39375
  if (target === "codex")
39211
- return join10(scope === "project" ? process.cwd() : homedir5(), dir, "hooks.json");
39376
+ return join10(scope === "project" ? process.cwd() : homedir6(), dir, "hooks.json");
39212
39377
  if (scope === "project") {
39213
39378
  return target === "codewith" ? join10(process.cwd(), dir, "config.toml") : join10(process.cwd(), dir, "settings.json");
39214
39379
  }
39215
- return target === "codewith" ? join10(homedir5(), dir, "config.toml") : join10(homedir5(), dir, "settings.json");
39380
+ return target === "codewith" ? join10(homedir6(), dir, "config.toml") : join10(homedir6(), dir, "settings.json");
39216
39381
  }
39217
39382
  function getHookPath(name) {
39218
39383
  const shortName = shortHookName(name);
@@ -39222,7 +39387,7 @@ function getHookPath(name) {
39222
39387
  return join10(HOOKS_DIR, normalizeHookName(shortName));
39223
39388
  }
39224
39389
  function hookExists(name) {
39225
- return existsSync10(getHookPath(name));
39390
+ return existsSync11(getHookPath(name));
39226
39391
  }
39227
39392
  var settingsPreimages = new WeakMap;
39228
39393
  function readSettings2(scope = "global", target = "claude") {
@@ -39230,7 +39395,7 @@ function readSettings2(scope = "global", target = "claude") {
39230
39395
  if (target === "codex")
39231
39396
  return readCodexSettings(path);
39232
39397
  try {
39233
- if (existsSync10(path)) {
39398
+ if (existsSync11(path)) {
39234
39399
  const text3 = readFileSync7(path, "utf-8"), value = JSON.parse(text3);
39235
39400
  settingsPreimages.set(value, { path, text: text3 });
39236
39401
  return value;
@@ -39239,14 +39404,14 @@ function readSettings2(scope = "global", target = "claude") {
39239
39404
  console.warn("[hooks] Cannot read hook settings; repair the selected file before installation.");
39240
39405
  }
39241
39406
  const empty = {};
39242
- if (!existsSync10(path))
39407
+ if (!existsSync11(path))
39243
39408
  settingsPreimages.set(empty, { path, text: null });
39244
39409
  return empty;
39245
39410
  }
39246
39411
  function settingsIdentity(path) {
39247
- if (existsSync10(path))
39412
+ if (existsSync11(path))
39248
39413
  return realpathSync5(path);
39249
- return existsSync10(dirname7(path)) ? join10(realpathSync5(dirname7(path)), basename3(path)) : resolve7(path);
39414
+ return existsSync11(dirname7(path)) ? join10(realpathSync5(dirname7(path)), basename3(path)) : resolve7(path);
39250
39415
  }
39251
39416
  function writeSettings(settings, scope = "global", target = "claude", expectedSettingsSha256) {
39252
39417
  const path = getSettingsPath(scope, target);
@@ -39258,21 +39423,21 @@ function writeSettings(settings, scope = "global", target = "claude", expectedSe
39258
39423
  throw new Error("Hook settings require a valid captured preimage.");
39259
39424
  assertSettingsPreimage(captured.text, expectedSettingsSha256);
39260
39425
  }
39261
- if (target === "claude" && settingsIdentity(path) === settingsIdentity(join10(homedir5(), ".claude/settings.json"))) {
39426
+ if (target === "claude" && settingsIdentity(path) === settingsIdentity(join10(homedir6(), ".claude/settings.json"))) {
39262
39427
  const before = settingsPreimages.get(settings);
39263
39428
  if (!before || before.path !== path)
39264
39429
  throw new Error("Claude settings require a valid captured preimage before installation.");
39265
- const current2 = existsSync10(path) ? readFileSync7(path, "utf8") : null;
39430
+ const current2 = existsSync11(path) ? readFileSync7(path, "utf8") : null;
39266
39431
  if (current2 !== before.text)
39267
39432
  throw new Error("Claude settings changed during installation; no settings were written.");
39268
39433
  if (coordinateClaudeSettings(before.text, JSON.stringify(settings, null, 2) + `
39269
- `, { home: homedir5() }))
39434
+ `, { home: homedir6() }))
39270
39435
  return;
39271
39436
  }
39272
39437
  if (expectedSettingsSha256 !== undefined)
39273
39438
  return writeHookSettingsPreimage(path, settings, captured.text);
39274
39439
  const dir = dirname7(path);
39275
- if (!existsSync10(dir)) {
39440
+ if (!existsSync11(dir)) {
39276
39441
  mkdirSync6(dir, { recursive: true });
39277
39442
  }
39278
39443
  writeFileSync5(path, JSON.stringify(settings, null, 2) + `
@@ -39371,7 +39536,7 @@ function buildCodewithTomlFragment(name, profile, mementos) {
39371
39536
  function readCodewithConfig(scope, configPath) {
39372
39537
  const path = getSettingsPath(scope, "codewith", configPath);
39373
39538
  try {
39374
- return existsSync10(path) ? readFileSync7(path, "utf-8") : "";
39539
+ return existsSync11(path) ? readFileSync7(path, "utf-8") : "";
39375
39540
  } catch {
39376
39541
  return "";
39377
39542
  }
@@ -39379,9 +39544,9 @@ function readCodewithConfig(scope, configPath) {
39379
39544
  function appendCodewithFragment(fragment, scope, configPath) {
39380
39545
  const path = getSettingsPath(scope, "codewith", configPath);
39381
39546
  const dir = dirname7(path);
39382
- if (!existsSync10(dir))
39547
+ if (!existsSync11(dir))
39383
39548
  mkdirSync6(dir, { recursive: true });
39384
- const existing = existsSync10(path) ? readFileSync7(path, "utf-8") : "";
39549
+ const existing = existsSync11(path) ? readFileSync7(path, "utf-8") : "";
39385
39550
  const sep3 = existing.trim() ? `
39386
39551
 
39387
39552
  ` : "";
@@ -39577,8 +39742,40 @@ function previewNativeSafetyRegistration(target, scope = "global") {
39577
39742
  throw new Error("Native safety registration could not be rendered.");
39578
39743
  return result;
39579
39744
  }
39745
+ function previewNativeReadinessRegistration(target) {
39746
+ let result;
39747
+ registerHook("trash-readiness", "global", target, undefined, undefined, (settings) => {
39748
+ const before = target === "codex" ? capturedCodexSettings(settings) : settingsPreimages.get(settings)?.text;
39749
+ if (before === undefined)
39750
+ throw new Error("Hook settings require a valid captured preimage.");
39751
+ result = { before, after: JSON.stringify(settings, null, 2) + `
39752
+ `, command: installedNativeReadinessCommand(target) };
39753
+ });
39754
+ if (!result)
39755
+ throw new Error("Native readiness registration could not be rendered.");
39756
+ return result;
39757
+ }
39758
+ function installNativeReadinessRegistration(target, expectedSettingsSha256) {
39759
+ if (!/^(?:absent|[a-f0-9]{64})$/.test(expectedSettingsSha256))
39760
+ throw new Error("Exact predecessor required");
39761
+ registerHook("trash-readiness", "global", target, undefined, undefined, (settings, scope, selected) => writeSettings(settings, scope, selected, expectedSettingsSha256));
39762
+ return { success: true };
39763
+ }
39580
39764
  function registerHook(name, scope = "global", target = "claude", profile, mementos, write = writeSettings) {
39581
- const meta3 = isNativeSafetyName(name) ? getHook(name) : resolveHookMeta(name);
39765
+ const readiness = name === "trash-readiness";
39766
+ if (readiness && (target === "gemini" || profile || scope !== "global"))
39767
+ throw new Error("Unsupported readiness target");
39768
+ const meta3 = readiness ? {
39769
+ name,
39770
+ event: "SessionStart",
39771
+ matcher: "^(startup|resume|clear|fork)$",
39772
+ timeoutSeconds: 20,
39773
+ displayName: "Trash Readiness",
39774
+ description: "Read-only native startup readiness",
39775
+ version: "0.1.0",
39776
+ category: "Environment",
39777
+ tags: ["trash", "startup"]
39778
+ } : isNativeSafetyName(name) ? getHook(name) : resolveHookMeta(name);
39582
39779
  if (!meta3)
39583
39780
  return;
39584
39781
  const eventKeys = getHookEvents(meta3).map((event) => {
@@ -39595,18 +39792,20 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
39595
39792
  const settings = readSettings2(scope, target);
39596
39793
  if (!settings.hooks)
39597
39794
  settings.hooks = {};
39598
- if (isNativeSafetyName(name)) {
39795
+ if (isNativeSafetyName(name) || readiness) {
39599
39796
  for (const groups of Object.values(settings.hooks))
39600
39797
  for (const group of groups)
39601
39798
  for (const hook of group.hooks ?? []) {
39602
39799
  if (typeof hook.command === "string" && /(?:^|\s)hasna-native-safety-v\d+(?:\s|$)/.test(hook.command) && !registrationName(hook.command)) {
39603
39800
  throw new Error("Unknown managed safety definition; reconcile its exact version before replacing it.");
39604
39801
  }
39802
+ if (readiness && typeof hook.command === "string" && hook.command.includes("hasna-native-readiness-v") && registrationName(hook.command) !== "trash-readiness")
39803
+ throw new Error("Unknown managed readiness definition");
39605
39804
  }
39606
39805
  }
39607
39806
  const retained = name === "mementos-context" && mementos === undefined ? retainedMementosRegistration(Object.values(settings.hooks).flatMap((groups) => Array.isArray(groups) ? groups.flatMap((group) => Array.isArray(group.hooks) ? group.hooks.map((hook) => hook.command).filter((command) => typeof command === "string") : []) : [])) : undefined;
39608
- const hookCommand = isNativeSafetyName(name) ? installedNativeSafetyCommand(name) : (profile ? `hooks run ${name} --profile ${profile}` : `hooks run ${name}`) + mementosCommandSuffix(mementos ?? retained);
39609
- if (target === "codex" || target === "claude" && isNativeSafetyName(name)) {
39807
+ const hookCommand = readiness ? installedNativeReadinessCommand(target) : isNativeSafetyName(name) ? installedNativeSafetyCommand(name) : (profile ? `hooks run ${name} --profile ${profile}` : `hooks run ${name}`) + mementosCommandSuffix(mementos ?? retained);
39808
+ if (target === "codex" || target === "claude" && (isNativeSafetyName(name) || readiness)) {
39610
39809
  const positions = [];
39611
39810
  for (const [event, groups] of Object.entries(settings.hooks)) {
39612
39811
  for (const entry of groups) {
@@ -39616,8 +39815,8 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
39616
39815
  if (match && (match[1] ?? match[2]) === name || registrationName(command) === name) {
39617
39816
  if (registrationName(command) !== name)
39618
39817
  throw new Error("Unrecognized Codex registration; reconcile it before updating this hook.");
39619
- if (isNativeSafetyName(name) && entry.hooks[index].async !== undefined && entry.hooks[index].async !== false)
39620
- throw new Error("Asynchronous safety hooks cannot block; reconcile this registration before updating it.");
39818
+ if ((isNativeSafetyName(name) || readiness) && (entry.hooks[index].async !== undefined && entry.hooks[index].async !== false || entry.hooks[index].asyncRewake === true))
39819
+ throw new Error("Asynchronous managed native hooks are refused; synchronous registration is required");
39621
39820
  positions.push({ event, entry, index });
39622
39821
  }
39623
39822
  }
@@ -39795,7 +39994,7 @@ function codewithHasHookEntryInText(configText, name) {
39795
39994
  }
39796
39995
  function codewithHasHookEntry(name, scope = "global") {
39797
39996
  const path = getSettingsPath(scope, "codewith");
39798
- if (!existsSync10(path))
39997
+ if (!existsSync11(path))
39799
39998
  return false;
39800
39999
  try {
39801
40000
  return codewithHasHookEntryInText(readFileSync7(path, "utf-8"), name);
@@ -39843,7 +40042,7 @@ function uninstallHook(name, scope = "global", target = "claude") {
39843
40042
  const registrationsRemaining = [];
39844
40043
  if (target === "codewith" || target === "all") {
39845
40044
  const codewithPath = getSettingsPath(scope, "codewith");
39846
- if (existsSync10(codewithPath)) {
40045
+ if (existsSync11(codewithPath)) {
39847
40046
  const before = readFileSync7(codewithPath, "utf-8");
39848
40047
  const after = removeCodewithHookEntry(before, shortName);
39849
40048
  if (after.removed) {
@@ -39886,7 +40085,7 @@ function uninstallHook(name, scope = "global", target = "claude") {
39886
40085
  // src/index.ts
39887
40086
  init_profiles();
39888
40087
  init_config();
39889
- import { existsSync as existsSync15, readFileSync as readFileSync26 } from "fs";
40088
+ import { existsSync as existsSync16, readFileSync as readFileSync25 } from "fs";
39890
40089
 
39891
40090
  // src/lib/transport.ts
39892
40091
  init_transport();
@@ -40026,15 +40225,15 @@ init_manifest();
40026
40225
  // src/lib/custom-install.ts
40027
40226
  init_config();
40028
40227
  init_manifest();
40029
- import { existsSync as existsSync12, mkdirSync as mkdirSync11, mkdtempSync, readFileSync as readFileSync14, rmSync as rmSync2 } from "fs";
40030
- import { join as join17 } from "path";
40228
+ import { existsSync as existsSync13, mkdirSync as mkdirSync11, mkdtempSync, readFileSync as readFileSync12, rmSync as rmSync2 } from "fs";
40229
+ import { join as join13 } from "path";
40031
40230
  import { tmpdir } from "os";
40032
40231
  function manifestAt(dir) {
40033
- const path = join17(dir, "manifest.json");
40034
- if (!existsSync12(path))
40232
+ const path = join13(dir, "manifest.json");
40233
+ if (!existsSync13(path))
40035
40234
  return;
40036
40235
  try {
40037
- return parseManifest(readFileSync14(path, "utf-8"));
40236
+ return parseManifest(readFileSync12(path, "utf-8"));
40038
40237
  } catch {
40039
40238
  return;
40040
40239
  }
@@ -40053,8 +40252,8 @@ function writeFromManifest(manifest, srcDir, source, kind) {
40053
40252
  return { name, version: manifest.version, kind, source, dir, scriptPath };
40054
40253
  }
40055
40254
  async function cloneToTemp(gitUrl) {
40056
- const tmp = mkdtempSync(join17(tmpdir(), "hooks-install-"));
40057
- const proc = Bun.spawn(["git", "clone", "--depth", "1", "--quiet", gitUrl, join17(tmp, "repo")], {
40255
+ const tmp = mkdtempSync(join13(tmpdir(), "hooks-install-"));
40256
+ const proc = Bun.spawn(["git", "clone", "--depth", "1", "--quiet", gitUrl, join13(tmp, "repo")], {
40058
40257
  stdout: "pipe",
40059
40258
  stderr: "pipe"
40060
40259
  });
@@ -40067,7 +40266,7 @@ async function cloneToTemp(gitUrl) {
40067
40266
  rmSync2(tmp, { recursive: true, force: true });
40068
40267
  throw new Error(`git clone failed (${exitCode}): ${stderr.trim()}`);
40069
40268
  }
40070
- return join17(tmp, "repo");
40269
+ return join13(tmp, "repo");
40071
40270
  }
40072
40271
  function isGitUrl(value) {
40073
40272
  return value.startsWith("git@") || value.startsWith("ssh://") || value.startsWith("file://") || value.endsWith(".git");
@@ -40077,7 +40276,7 @@ function isCustomSource(value) {
40077
40276
  return true;
40078
40277
  if (value.startsWith("http://") || value.startsWith("https://"))
40079
40278
  return true;
40080
- return existsSync12(value);
40279
+ return existsSync13(value);
40081
40280
  }
40082
40281
  async function installCustomSource(source) {
40083
40282
  if (isGitUrl(source)) {
@@ -40091,11 +40290,11 @@ async function installCustomSource(source) {
40091
40290
  if (source.startsWith("http://") || source.startsWith("https://")) {
40092
40291
  return installManifestUrl(source);
40093
40292
  }
40094
- if (!existsSync12(source)) {
40293
+ if (!existsSync13(source)) {
40095
40294
  throw new Error(`No such local path: ${source}`);
40096
40295
  }
40097
40296
  if (source.endsWith("manifest.json")) {
40098
- const manifest = parseManifest(readFileSync14(source, "utf-8"));
40297
+ const manifest = parseManifest(readFileSync12(source, "utf-8"));
40099
40298
  return writeFromManifest(manifest, source.substring(0, source.lastIndexOf("/")), source, "local");
40100
40299
  }
40101
40300
  return copyManifestDir(source, source, "local");
@@ -40125,16 +40324,16 @@ async function installManifestUrl(url2) {
40125
40324
  return { name, version: manifest.version, kind: "url", source: url2, dir, scriptPath };
40126
40325
  }
40127
40326
  // src/lib/sync.ts
40128
- import { createHash as createHash6 } from "crypto";
40129
- import { readFileSync as readFileSync15 } from "fs";
40327
+ import { createHash as createHash7 } from "crypto";
40328
+ import { readFileSync as readFileSync14 } from "fs";
40130
40329
  init_db();
40131
40330
  init_store();
40132
40331
  init_manifest();
40133
40332
  function sha256OfText(text3) {
40134
- return createHash6("sha256").update(text3).digest("hex");
40333
+ return createHash7("sha256").update(text3).digest("hex");
40135
40334
  }
40136
40335
  function sha256OfPath(path) {
40137
- return createHash6("sha256").update(readFileSync15(path)).digest("hex");
40336
+ return createHash7("sha256").update(readFileSync14(path)).digest("hex");
40138
40337
  }
40139
40338
  function collectBundledCatalog() {
40140
40339
  const out = [];
@@ -40189,13 +40388,13 @@ async function fetchRemoteState(apiUrl, apiKey) {
40189
40388
  }
40190
40389
  return { catalog: catalog.hooks, remoteLock };
40191
40390
  }
40192
- function classifyRemotePin(pin, remoteVersion, remoteSha) {
40193
- const pinnedLocally = pin !== undefined && pin.version === remoteVersion && pin.sha256 === remoteSha;
40391
+ function classifyRemotePin(pin2, remoteVersion, remoteSha) {
40392
+ const pinnedLocally = pin2 !== undefined && pin2.version === remoteVersion && pin2.sha256 === remoteSha;
40194
40393
  if (pinnedLocally)
40195
40394
  return "unchanged";
40196
- if (pin !== undefined && pin.pinned === true && pin.version !== remoteVersion)
40395
+ if (pin2 !== undefined && pin2.pinned === true && pin2.version !== remoteVersion)
40197
40396
  return "unchanged";
40198
- if (pin !== undefined)
40397
+ if (pin2 !== undefined)
40199
40398
  return "updated";
40200
40399
  return "added";
40201
40400
  }
@@ -40205,7 +40404,7 @@ function computeDiff(catalog, remoteLock) {
40205
40404
  for (const entry of catalog) {
40206
40405
  if (!entry.name || !entry.version || !entry.sha256)
40207
40406
  continue;
40208
- const pin = local.hooks[entry.name];
40407
+ const pin2 = local.hooks[entry.name];
40209
40408
  if (remoteLock) {
40210
40409
  const remotePin = remoteLock.hooks[entry.name];
40211
40410
  if (!remotePin) {
@@ -40215,9 +40414,9 @@ function computeDiff(catalog, remoteLock) {
40215
40414
  if (remotePin.version !== entry.version) {
40216
40415
  throw new Error(`remote registry is ambiguous for '${entry.name}': lock pins ${remotePin.version} but catalog latest is ${entry.version}. ` + `Fix the registry (or its lock) before syncing \u2014 nothing was changed.`);
40217
40416
  }
40218
- diff[classifyRemotePin(pin, remotePin.version, remotePin.sha256)].push(entry.name);
40417
+ diff[classifyRemotePin(pin2, remotePin.version, remotePin.sha256)].push(entry.name);
40219
40418
  } else {
40220
- diff[classifyRemotePin(pin, entry.version, entry.sha256)].push(entry.name);
40419
+ diff[classifyRemotePin(pin2, entry.version, entry.sha256)].push(entry.name);
40221
40420
  }
40222
40421
  }
40223
40422
  return diff;
@@ -40435,8 +40634,8 @@ async function installPinnedFromBundled(name, version2) {
40435
40634
  // src/serve.ts
40436
40635
  init_manifest();
40437
40636
  init_store();
40438
- import { readFileSync as readFileSync17 } from "fs";
40439
- import { dirname as dirname17, join as join19 } from "path";
40637
+ import { readFileSync as readFileSync15 } from "fs";
40638
+ import { dirname as dirname17, join as join17 } from "path";
40440
40639
  import { fileURLToPath as fileURLToPath4 } from "url";
40441
40640
 
40442
40641
  // src/lib/secure-compare.ts
@@ -40811,8 +41010,8 @@ var SERVE_HOST = "127.0.0.1";
40811
41010
  var SERVE_SERVICE_NAME = "hooks-registry";
40812
41011
  function packageVersion() {
40813
41012
  try {
40814
- const pkgPath = join19(dirname17(fileURLToPath4(import.meta.url)), "..", "package.json");
40815
- return JSON.parse(readFileSync17(pkgPath, "utf-8")).version || "0.0.0";
41013
+ const pkgPath = join17(dirname17(fileURLToPath4(import.meta.url)), "..", "package.json");
41014
+ return JSON.parse(readFileSync15(pkgPath, "utf-8")).version || "0.0.0";
40816
41015
  } catch {
40817
41016
  return "0.0.0";
40818
41017
  }
@@ -40913,8 +41112,8 @@ function handleServeRequest(req, apiKey) {
40913
41112
  for (const entry of catalog)
40914
41113
  byVersion[entry.name] = entry.versions;
40915
41114
  const hooks = {};
40916
- for (const [name, pin] of Object.entries(lock.hooks)) {
40917
- hooks[name] = { ...pin, versions: byVersion[name] ?? [pin.version] };
41115
+ for (const [name, pin2] of Object.entries(lock.hooks)) {
41116
+ hooks[name] = { ...pin2, versions: byVersion[name] ?? [pin2.version] };
40918
41117
  }
40919
41118
  return json2({ hooks });
40920
41119
  });
@@ -41057,23 +41256,23 @@ async function provisionCloudflareResources(options) {
41057
41256
  return result;
41058
41257
  }
41059
41258
  // src/lib/codex-safety-trust.ts
41060
- import { createHash as createHash12, randomUUID as randomUUID3 } from "crypto";
41061
- import { constants as constants12, closeSync as closeSync7, fsyncSync as fsyncSync5, fstatSync as fstatSync6, lstatSync as lstatSync15, mkdirSync as mkdirSync13, openSync as openSync7, readFileSync as readFileSync21, realpathSync as realpathSync10, writeFileSync as writeFileSync11 } from "fs";
41062
- import { dirname as dirname21, isAbsolute as isAbsolute18, join as join24, relative as relative2 } from "path";
41063
- import { homedir as homedir7 } from "os";
41259
+ import { createHash as createHash17, randomUUID as randomUUID3 } from "crypto";
41260
+ import { constants as constants14, closeSync as closeSync15, fsyncSync as fsyncSync5, fstatSync as fstatSync7, lstatSync as lstatSync15, mkdirSync as mkdirSync13, openSync as openSync15, readFileSync as readFileSync20, realpathSync as realpathSync10, writeFileSync as writeFileSync11 } from "fs";
41261
+ import { dirname as dirname21, isAbsolute as isAbsolute18, join as join20, relative as relative2 } from "path";
41262
+ import { homedir as homedir8 } from "os";
41064
41263
 
41065
41264
  // src/lib/codex-safety-check.ts
41066
41265
  import { spawn, execFile as execFile2 } from "child_process";
41067
- import { createHash as createHash7 } from "crypto";
41068
- import { constants as constants4, openSync as openSync6, closeSync as closeSync6, fstatSync as fstatSync5, lstatSync as lstatSync10, readFileSync as readFileSync20, realpathSync as realpathSync9 } from "fs";
41266
+ import { createHash as createHash12 } from "crypto";
41267
+ import { constants as constants12, openSync as openSync7, closeSync as closeSync7, fstatSync as fstatSync6, lstatSync as lstatSync10, readFileSync as readFileSync19, realpathSync as realpathSync9 } from "fs";
41069
41268
  import { dirname as dirname20, isAbsolute as isAbsolute17, resolve as resolve17 } from "path";
41070
41269
  import { promisify } from "util";
41071
41270
 
41072
41271
  // src/native-safety.ts
41073
41272
  import { execFile } from "child_process";
41074
- import { closeSync as closeSync5, constants as constants3, existsSync as existsSync13, fstatSync as fstatSync4, fsyncSync, linkSync as linkSync3, lstatSync as lstatSync9, mkdirSync as mkdirSync12, openSync as openSync5, readFileSync as readFileSync19, realpathSync as realpathSync8, renameSync as renameSync3, unlinkSync as unlinkSync5, writeFileSync as writeFileSync10 } from "fs";
41075
- import { homedir as homedir6 } from "os";
41076
- import { dirname as dirname19, isAbsolute as isAbsolute15, join as join20, resolve as resolve15 } from "path";
41273
+ import { closeSync as closeSync6, constants as constants4, existsSync as existsSync14, fstatSync as fstatSync5, fsyncSync, linkSync as linkSync3, lstatSync as lstatSync9, mkdirSync as mkdirSync12, openSync as openSync6, readFileSync as readFileSync17, realpathSync as realpathSync8, renameSync as renameSync3, unlinkSync as unlinkSync5, writeFileSync as writeFileSync10 } from "fs";
41274
+ import { homedir as homedir7 } from "os";
41275
+ import { dirname as dirname19, isAbsolute as isAbsolute15, join as join19, resolve as resolve15 } from "path";
41077
41276
  class NativeSafetyError extends Error {
41078
41277
  code;
41079
41278
  reason;
@@ -41143,7 +41342,7 @@ async function evaluateRecord(event, record3, signal, executionAgent) {
41143
41342
  } catch {}
41144
41343
  throw new NativeSafetyError("guard_failed");
41145
41344
  }
41146
- async function verifyNativeSafetyCommand(command, home = homedir6()) {
41345
+ async function verifyNativeSafetyCommand(command, home = homedir7()) {
41147
41346
  home = canonicalHome(home);
41148
41347
  const record3 = parseRecord(JSON.stringify({ schema: "hasna.hooks.native-safety.v1", harness: "sumi", command }), home);
41149
41348
  const verdict = await evaluateRecord({ hook_event_name: "PreToolUse", tool_name: "Bash", cwd: home, tool_input: { command: "rm -- .hasna-trash-verification-only" } }, record3);
@@ -41167,7 +41366,7 @@ function need5(condition, code2) {
41167
41366
  if (!condition)
41168
41367
  throw new CodexSafetyError(code2);
41169
41368
  }
41170
- var digest3 = (bytes) => createHash7("sha256").update(bytes).digest("hex");
41369
+ var digest3 = (bytes) => createHash12("sha256").update(bytes).digest("hex");
41171
41370
  function nativeHeader(bytes) {
41172
41371
  if (bytes.length < 64 || !["arm64", "x64"].includes(process.arch))
41173
41372
  return false;
@@ -41192,16 +41391,16 @@ function executable(command) {
41192
41391
  if (parent === dirname20(parent))
41193
41392
  break;
41194
41393
  }
41195
- const fd = openSync6(path, constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
41394
+ const fd = openSync7(path, constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK);
41196
41395
  try {
41197
- const stat2 = fstatSync5(fd);
41396
+ const stat2 = fstatSync6(fd);
41198
41397
  need5(stat2.isFile() && [0, process.getuid?.()].includes(stat2.uid) && !(stat2.mode & 18) && !!(stat2.mode & 64) && stat2.size <= 256000000, "native_unsafe_executable");
41199
- const bytes = readFileSync20(fd);
41398
+ const bytes = readFileSync19(fd);
41200
41399
  need5(bytes.length <= 256000000, "native_unavailable");
41201
41400
  need5(nativeHeader(bytes), "native_executable_required");
41202
41401
  return { path, sha256: digest3(bytes) };
41203
41402
  } finally {
41204
- closeSync6(fd);
41403
+ closeSync7(fd);
41205
41404
  }
41206
41405
  } catch (error61) {
41207
41406
  if (error61 instanceof CodexSafetyError)
@@ -41346,7 +41545,7 @@ function supportedDiscoveryWarnings(hooks, warnings) {
41346
41545
  }
41347
41546
  return warnings.every((warning) => typeof warning === "string" && allowed.delete(warning));
41348
41547
  }
41349
- function selectCodexSafety(discovery, configuration, home, cwd, featureEnabled, requireReady = true) {
41548
+ function selectCodexSafety(discovery, configuration, home, cwd, featureEnabled, requireReady = true, readiness = false) {
41350
41549
  const data = discovery?.data;
41351
41550
  need5(Array.isArray(data) && data.length === 1 && data[0]?.cwd === cwd && Array.isArray(data[0]?.hooks) && data[0].hooks.length <= 1e4 && Array.isArray(data[0].errors) && data[0].errors.length === 0 && Array.isArray(data[0].warnings) && supportedDiscoveryWarnings(data[0].hooks, data[0].warnings), "native_discovery_failed");
41352
41551
  const config2 = configuration?.config;
@@ -41357,12 +41556,14 @@ function selectCodexSafety(discovery, configuration, home, cwd, featureEnabled,
41357
41556
  if (/\bhasna-native-safety-v\d+\b/.test(hook2.command))
41358
41557
  need5(nativeSafetyCommandBinding(hook2.command), "guard_definition_invalid");
41359
41558
  need5(!/^(?:hooks run trash-guard|hook-trash-guard)(?:\s|$)/.test(hook2.command.trim()), "guard_definition_invalid");
41559
+ if (readiness && hook2.command.includes("hasna-native-readiness-v"))
41560
+ need5(nativeReadinessCommandBinding(hook2.command), "guard_definition_invalid");
41360
41561
  }
41361
- const selected = data[0].hooks.filter((hook2) => typeof hook2?.command === "string" && nativeSafetyCommandBinding(hook2.command)?.name === "trash-guard");
41562
+ const selected = data[0].hooks.filter((hook2) => typeof hook2?.command === "string" && (readiness ? !!nativeReadinessCommandBinding(hook2.command) : nativeSafetyCommandBinding(hook2.command)?.name === "trash-guard"));
41362
41563
  need5(selected.length !== 0, "guard_missing");
41363
41564
  need5(selected.length === 1, "guard_ambiguous");
41364
- const hook = selected[0], binding = nativeSafetyCommandBinding(hook.command);
41365
- need5(binding.home === home && hook.eventName === "preToolUse" && hook.handlerType === "command" && hook.matcher === matcher && hook.timeoutSec === 10 && hook.async === false && hook.statusMessage === null && hook.additionalContextLimit === null && typeof hook.key === "string" && hook.key.length <= 4096 && typeof hook.sourcePath === "string" && isAbsolute17(hook.sourcePath) && hook.pluginId === null && hook.isManaged === false && ["user", "project"].includes(hook.source) && typeof hook.currentHash === "string" && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash), "guard_definition_invalid");
41565
+ const hook = selected[0], binding = readiness ? nativeReadinessCommandBinding(hook.command) : nativeSafetyCommandBinding(hook.command);
41566
+ need5(binding.home === home && (!readiness || binding?.provider === "codex") && hook.eventName === (readiness ? "sessionStart" : "preToolUse") && hook.handlerType === "command" && hook.matcher === (readiness ? "^(startup|resume|clear|fork)$" : matcher) && hook.timeoutSec === (readiness ? 20 : 10) && hook.async === false && hook.statusMessage === null && hook.additionalContextLimit === null && typeof hook.key === "string" && hook.key.length <= 4096 && typeof hook.sourcePath === "string" && isAbsolute17(hook.sourcePath) && hook.pluginId === null && hook.isManaged === false && ["user", "project"].includes(hook.source) && typeof hook.currentHash === "string" && /^sha256:[a-f0-9]{64}$/.test(hook.currentHash), "guard_definition_invalid");
41366
41567
  need5(typeof hook.enabled === "boolean" && ["trusted", "untrusted", "modified"].includes(hook.trustStatus), "guard_definition_invalid");
41367
41568
  if (requireReady) {
41368
41569
  need5(hook.enabled === true, "guard_disabled");
@@ -41395,7 +41596,7 @@ async function readCodexSafety(rpc, home, cwd, options = {}) {
41395
41596
  }
41396
41597
  } while (cursor !== null);
41397
41598
  }
41398
- return { guard: selectCodexSafety(hooks, configuration, home, cwd, enabled, options.requireReady ?? true), discovery: hooks, configuration };
41599
+ return { guard: selectCodexSafety(hooks, configuration, home, cwd, enabled, options.requireReady ?? true, options.readiness ?? false), discovery: hooks, configuration };
41399
41600
  }
41400
41601
 
41401
41602
  // src/lib/codex-safety-trust.ts
@@ -41413,7 +41614,7 @@ function need7(value, code2 = "native_changed") {
41413
41614
  if (!value)
41414
41615
  throw new CodexTrustError(code2);
41415
41616
  }
41416
- var sha4 = (value) => createHash12("sha256").update(value).digest("hex");
41617
+ var sha4 = (value) => createHash17("sha256").update(value).digest("hex");
41417
41618
  function canonical4(value) {
41418
41619
  if (Array.isArray(value))
41419
41620
  return "[" + value.map(canonical4).join(",") + "]";
@@ -41454,37 +41655,37 @@ function safeDirectory2(path) {
41454
41655
  }
41455
41656
  function readPrivate(path) {
41456
41657
  safeDirectory2(dirname21(path));
41457
- const fd = openSync7(path, constants12.O_RDONLY | constants12.O_NOFOLLOW | constants12.O_NONBLOCK);
41658
+ const fd = openSync15(path, constants14.O_RDONLY | constants14.O_NOFOLLOW | constants14.O_NONBLOCK);
41458
41659
  try {
41459
- const s = fstatSync6(fd);
41660
+ const s = fstatSync7(fd);
41460
41661
  need7(s.isFile() && s.uid === process.getuid?.() && !(s.mode & 63) && s.size <= 4194304, "preimage_unsafe");
41461
- const bytes = readFileSync21(fd);
41662
+ const bytes = readFileSync20(fd);
41462
41663
  need7(bytes.length <= 4194304, "preimage_unsafe");
41463
41664
  return bytes;
41464
41665
  } finally {
41465
- closeSync7(fd);
41666
+ closeSync15(fd);
41466
41667
  }
41467
41668
  }
41468
41669
  function syncDirectory(path) {
41469
- const fd = openSync7(path, constants12.O_RDONLY | constants12.O_DIRECTORY | constants12.O_NOFOLLOW);
41670
+ const fd = openSync15(path, constants14.O_RDONLY | constants14.O_DIRECTORY | constants14.O_NOFOLLOW);
41470
41671
  try {
41471
41672
  fsyncSync5(fd);
41472
41673
  } finally {
41473
- closeSync7(fd);
41674
+ closeSync15(fd);
41474
41675
  }
41475
41676
  }
41476
41677
  function exclusive(path, bytes) {
41477
- const fd = openSync7(path, constants12.O_CREAT | constants12.O_EXCL | constants12.O_WRONLY | constants12.O_NOFOLLOW, 384);
41678
+ const fd = openSync15(path, constants14.O_CREAT | constants14.O_EXCL | constants14.O_WRONLY | constants14.O_NOFOLLOW, 384);
41478
41679
  try {
41479
41680
  writeFileSync11(fd, bytes);
41480
41681
  fsyncSync5(fd);
41481
41682
  } finally {
41482
- closeSync7(fd);
41683
+ closeSync15(fd);
41483
41684
  }
41484
41685
  syncDirectory(dirname21(path));
41485
41686
  }
41486
- async function inspect(rpc, home, cwd) {
41487
- const observed = await readCodexSafety(rpc, home, cwd, { includeLayers: true, requireReady: false });
41687
+ async function inspect(rpc, home, cwd, readiness = false) {
41688
+ const observed = await readCodexSafety(rpc, home, cwd, { includeLayers: true, requireReady: false, readiness });
41488
41689
  const { guard, configuration, discovery } = observed;
41489
41690
  const layers = configuration?.layers;
41490
41691
  need7(Array.isArray(layers) && layers.length <= 1000, "native_config_invalid");
@@ -41501,7 +41702,7 @@ async function inspect(rpc, home, cwd) {
41501
41702
  need7(rawState === undefined || rawState && typeof rawState === "object" && !Array.isArray(rawState), "native_config_invalid");
41502
41703
  const desired = { ...rawState, enabled: true, trusted_hash: guard.currentHash };
41503
41704
  const planBase = {
41504
- schema: "hasna.hooks.codex-safety-trust/v1",
41705
+ schema: readiness ? "hasna.hooks.codex-readiness-trust/v1" : "hasna.hooks.codex-safety-trust/v1",
41505
41706
  nativeVersion: rpc.version,
41506
41707
  cwd,
41507
41708
  key: guard.key,
@@ -41509,37 +41710,44 @@ async function inspect(rpc, home, cwd) {
41509
41710
  configPath: path,
41510
41711
  configVersion: layer.version,
41511
41712
  action: guard.enabled && guard.trustStatus === "trusted" ? "unchanged" : "trust_and_enable",
41512
- guardVerified: true,
41713
+ guardVerified: !readiness,
41714
+ ...readiness ? { readinessRunnerVerified: true } : {},
41513
41715
  nativeAdoptionVerified: false
41514
41716
  };
41515
41717
  const plan = { ...planBase, planDigest: sha4(canonical4({ planBase, configuration, discovery, raw: sha4(before) })) };
41516
41718
  return { ...observed, layer, layers, canonicalPath, before, desired, plan };
41517
41719
  }
41518
41720
  async function context2(options, dependencies) {
41519
- const home = realpathSync10(options.home ?? homedir7()), cwd = realpathSync10(options.cwd ?? process.cwd());
41721
+ const home = realpathSync10(options.home ?? homedir8()), cwd = realpathSync10(options.cwd ?? process.cwd());
41520
41722
  need7(isAbsolute18(home) && isAbsolute18(cwd), "input_invalid");
41521
41723
  return { home, cwd, rpc: await (dependencies.connect ?? connectCodexSafety)({ ...options, home }) };
41522
41724
  }
41523
- async function planCodexSafetyTrust(options = {}, dependencies = {}) {
41725
+ async function verify(command, home, readiness, dependencies) {
41726
+ if (readiness)
41727
+ need7(nativeReadinessCommandVerified(command));
41728
+ else
41729
+ await (dependencies.verify ?? verifyNativeSafetyCommand)(command, home);
41730
+ }
41731
+ async function planTrust(options = {}, dependencies = {}) {
41524
41732
  const { home, cwd, rpc } = await context2(options, dependencies);
41525
41733
  try {
41526
- const before = await inspect(rpc, home, cwd);
41527
- await (dependencies.verify ?? verifyNativeSafetyCommand)(before.guard.command, home);
41528
- need7((await inspect(rpc, home, cwd)).plan.planDigest === before.plan.planDigest);
41734
+ const before = await inspect(rpc, home, cwd, options.readiness);
41735
+ await verify(before.guard.command, home, !!options.readiness, dependencies);
41736
+ need7((await inspect(rpc, home, cwd, options.readiness)).plan.planDigest === before.plan.planDigest);
41529
41737
  return before.plan;
41530
41738
  } finally {
41531
41739
  await rpc.close();
41532
41740
  }
41533
41741
  }
41534
- async function applyCodexSafetyTrust(options, dependencies = {}) {
41742
+ async function applyTrust(options, dependencies = {}) {
41535
41743
  need7(/^[a-f0-9]{64}$/.test(options.expectedPlanDigest), "input_invalid");
41536
41744
  const selected = { ...options }, { home, cwd, rpc } = await context2(selected, dependencies);
41537
41745
  let operationId;
41538
41746
  try {
41539
- const before = await inspect(rpc, home, cwd);
41747
+ const before = await inspect(rpc, home, cwd, selected.readiness);
41540
41748
  need7(before.plan.planDigest === selected.expectedPlanDigest);
41541
- await (dependencies.verify ?? verifyNativeSafetyCommand)(before.guard.command, home);
41542
- need7((await inspect(rpc, home, cwd)).plan.planDigest === before.plan.planDigest);
41749
+ await verify(before.guard.command, home, !!selected.readiness, dependencies);
41750
+ need7((await inspect(rpc, home, cwd, selected.readiness)).plan.planDigest === before.plan.planDigest);
41543
41751
  if (before.plan.action === "unchanged")
41544
41752
  return { ok: true, changed: false, ...before.plan };
41545
41753
  const expectedConfiguration = structuredClone(before.configuration);
@@ -41555,8 +41763,8 @@ async function applyCodexSafetyTrust(options, dependencies = {}) {
41555
41763
  expectedGuard.trustStatus = "trusted";
41556
41764
  operationId = randomUUID3();
41557
41765
  let directory2 = home;
41558
- for (const part of [".hasna", "hooks", "native", "codex-trash-trust", operationId]) {
41559
- directory2 = join24(directory2, part);
41766
+ for (const part of [".hasna", "hooks", "native", selected.readiness ? "codex-readiness-trust" : "codex-trash-trust", operationId]) {
41767
+ directory2 = join20(directory2, part);
41560
41768
  try {
41561
41769
  mkdirSync13(directory2, { mode: 448 });
41562
41770
  syncDirectory(dirname21(directory2));
@@ -41566,8 +41774,8 @@ async function applyCodexSafetyTrust(options, dependencies = {}) {
41566
41774
  }
41567
41775
  safeDirectory2(directory2);
41568
41776
  }
41569
- exclusive(join24(directory2, "config.before"), before.before);
41570
- exclusive(join24(directory2, "intent.json"), JSON.stringify({ operationId, plan: before.plan, configSHA256: sha4(before.before) }) + `
41777
+ exclusive(join20(directory2, "config.before"), before.before);
41778
+ exclusive(join20(directory2, "intent.json"), JSON.stringify({ operationId, plan: before.plan, configSHA256: sha4(before.before) }) + `
41571
41779
  `);
41572
41780
  need7(sha4(readPrivate(before.canonicalPath)) === sha4(before.before));
41573
41781
  const result = await rpc.request("config/batchWrite", {
@@ -41577,7 +41785,7 @@ async function applyCodexSafetyTrust(options, dependencies = {}) {
41577
41785
  edits: [{ keyPath: "hooks.state." + JSON.stringify(before.guard.key), value: before.desired, mergeStrategy: "replace" }]
41578
41786
  });
41579
41787
  need7(result?.status === "ok" && result.filePath === before.plan.configPath && result.overriddenMetadata === null && /^sha256:[a-f0-9]{64}$/.test(result.version));
41580
- const after = await inspect(rpc, home, cwd);
41788
+ const after = await inspect(rpc, home, cwd, selected.readiness);
41581
41789
  expectedLayer.version = result.version;
41582
41790
  need7(after.layer.version === result.version && after.plan.action === "unchanged" && mergedConfiguration(after.configuration.config) === mergedConfiguration(expectedConfiguration.config) && canonical4(after.configuration.layers) === canonical4(expectedConfiguration.layers) && canonical4(after.discovery) === canonical4(expectedDiscovery));
41583
41791
  const receipt = {
@@ -41586,12 +41794,13 @@ async function applyCodexSafetyTrust(options, dependencies = {}) {
41586
41794
  operationId,
41587
41795
  planDigest: before.plan.planDigest,
41588
41796
  configVersion: result.version,
41589
- guardVerified: true,
41797
+ guardVerified: !selected.readiness,
41798
+ ...selected.readiness ? { readinessRunnerVerified: true } : {},
41590
41799
  nativeDiscoveryVerified: true,
41591
41800
  nativeAdoptionVerified: false,
41592
41801
  unrelatedConfigurationPreserved: true
41593
41802
  };
41594
- exclusive(join24(directory2, "receipt.json"), JSON.stringify(receipt) + `
41803
+ exclusive(join20(directory2, "receipt.json"), JSON.stringify(receipt) + `
41595
41804
  `);
41596
41805
  return receipt;
41597
41806
  } catch (error61) {
@@ -41602,11 +41811,13 @@ async function applyCodexSafetyTrust(options, dependencies = {}) {
41602
41811
  await rpc.close();
41603
41812
  }
41604
41813
  }
41814
+ var planCodexSafetyTrust = (options = {}, dependencies = {}) => planTrust({ ...options, readiness: false }, dependencies);
41815
+ var applyCodexSafetyTrust = (options, dependencies = {}) => applyTrust({ ...options, readiness: false }, dependencies);
41605
41816
  // src/lib/claude-safety-check.ts
41606
- import { constants as constants14, closeSync as closeSync15, fstatSync as fstatSync7, lstatSync as lstatSync21, openSync as openSync15, readFileSync as readFileSync23, realpathSync as realpathSync11 } from "fs";
41607
- import { createHash as createHash17 } from "crypto";
41608
- import { dirname as dirname22, isAbsolute as isAbsolute19, join as join27, resolve as resolve18 } from "path";
41609
- import { homedir as homedir8 } from "os";
41817
+ import { constants as constants15, closeSync as closeSync17, fstatSync as fstatSync15, lstatSync as lstatSync21, openSync as openSync17, readFileSync as readFileSync21, realpathSync as realpathSync11 } from "fs";
41818
+ import { createHash as createHash19 } from "crypto";
41819
+ import { dirname as dirname22, isAbsolute as isAbsolute19, join as join24, resolve as resolve18 } from "path";
41820
+ import { homedir as homedir9 } from "os";
41610
41821
  class ClaudeSafetyError extends Error {
41611
41822
  code;
41612
41823
  constructor(code2) {
@@ -41619,7 +41830,7 @@ function need8(value, code2) {
41619
41830
  if (!value)
41620
41831
  throw new ClaudeSafetyError(code2);
41621
41832
  }
41622
- var sha6 = (value) => createHash17("sha256").update(value).digest("hex");
41833
+ var sha6 = (value) => createHash19("sha256").update(value).digest("hex");
41623
41834
  function read3(path) {
41624
41835
  try {
41625
41836
  const resolved = realpathSync11(path);
@@ -41629,15 +41840,15 @@ function read3(path) {
41629
41840
  if (dirname22(parent) === parent)
41630
41841
  break;
41631
41842
  }
41632
- const fd = openSync15(resolved, constants14.O_RDONLY | constants14.O_NOFOLLOW | constants14.O_NONBLOCK);
41843
+ const fd = openSync17(resolved, constants15.O_RDONLY | constants15.O_NOFOLLOW | constants15.O_NONBLOCK);
41633
41844
  try {
41634
- const s = fstatSync7(fd);
41845
+ const s = fstatSync15(fd);
41635
41846
  need8(s.isFile() && [0, process.getuid?.()].includes(s.uid) && !(s.mode & 18) && s.size <= 1048576, "settings_unsafe");
41636
- const raw = readFileSync23(fd, "utf8");
41847
+ const raw = readFileSync21(fd, "utf8");
41637
41848
  need8(Buffer.byteLength(raw) <= 1048576, "settings_unsafe");
41638
41849
  return { path, resolved, sha256: sha6(raw), raw };
41639
41850
  } finally {
41640
- closeSync15(fd);
41851
+ closeSync17(fd);
41641
41852
  }
41642
41853
  } catch (error61) {
41643
41854
  if (error61.code === "ENOENT")
@@ -41648,11 +41859,11 @@ function read3(path) {
41648
41859
  }
41649
41860
  }
41650
41861
  async function verifyClaudeSafetyConfiguration(options = {}, dependencies = {}) {
41651
- const home = realpathSync11(options.home ?? homedir8()), cwd = realpathSync11(options.cwd ?? process.cwd());
41862
+ const home = realpathSync11(options.home ?? homedir9()), cwd = realpathSync11(options.cwd ?? process.cwd());
41652
41863
  const paths = [
41653
- options.userSettingsPath ?? join27(home, ".claude/settings.json"),
41654
- join27(cwd, ".claude/settings.json"),
41655
- options.localSettingsPath ?? join27(cwd, ".claude/settings.local.json")
41864
+ options.userSettingsPath ?? join24(home, ".claude/settings.json"),
41865
+ join24(cwd, ".claude/settings.json"),
41866
+ options.localSettingsPath ?? join24(cwd, ".claude/settings.local.json")
41656
41867
  ];
41657
41868
  need8(paths.every((path) => isAbsolute19(path) && resolve18(path) === path), "settings_unsafe");
41658
41869
  const inspect2 = () => [...new Set(paths)].map(read3);
@@ -41713,10 +41924,10 @@ async function verifyClaudeSafetyConfiguration(options = {}, dependencies = {})
41713
41924
  };
41714
41925
  }
41715
41926
  // src/lib/native-safety-registration.ts
41716
- import { createHash as createHash19, randomUUID as randomUUID5 } from "crypto";
41717
- import { constants as constants15, closeSync as closeSync17, fstatSync as fstatSync15, fsyncSync as fsyncSync7, lstatSync as lstatSync23, mkdirSync as mkdirSync14, openSync as openSync17, readFileSync as readFileSync25, realpathSync as realpathSync12, writeFileSync as writeFileSync12 } from "fs";
41718
- import { basename as basename9, dirname as dirname23, isAbsolute as isAbsolute20, join as join29, relative as relative3, resolve as resolve19 } from "path";
41719
- import { homedir as homedir9 } from "os";
41927
+ import { createHash as createHash21, randomUUID as randomUUID5 } from "crypto";
41928
+ import { constants as constants16, closeSync as closeSync18, fstatSync as fstatSync17, fsyncSync as fsyncSync7, lstatSync as lstatSync23, mkdirSync as mkdirSync14, openSync as openSync18, readFileSync as readFileSync23, realpathSync as realpathSync12, writeFileSync as writeFileSync12 } from "fs";
41929
+ import { basename as basename9, dirname as dirname23, isAbsolute as isAbsolute20, join as join27, relative as relative3, resolve as resolve19 } from "path";
41930
+ import { homedir as homedir10 } from "os";
41720
41931
  class NativeRegistrationError extends Error {
41721
41932
  code;
41722
41933
  operationId;
@@ -41731,7 +41942,7 @@ var need9 = (value, code2) => {
41731
41942
  if (!value)
41732
41943
  throw new NativeRegistrationError(code2);
41733
41944
  };
41734
- var sha7 = (value) => createHash19("sha256").update(value).digest("hex");
41945
+ var sha7 = (value) => createHash21("sha256").update(value).digest("hex");
41735
41946
  function canonical6(value) {
41736
41947
  if (Array.isArray(value))
41737
41948
  return "[" + value.map(canonical6).join(",") + "]";
@@ -41765,7 +41976,7 @@ function read5(path) {
41765
41976
  const suffix = [basename9(path)];
41766
41977
  for (;; ) {
41767
41978
  try {
41768
- resolved = join29(realpathSync12(ancestor), ...suffix);
41979
+ resolved = join27(realpathSync12(ancestor), ...suffix);
41769
41980
  break;
41770
41981
  } catch (missing2) {
41771
41982
  if (missing2.code !== "ENOENT" || dirname23(ancestor) === ancestor || suffix.length >= 64)
@@ -41787,22 +41998,22 @@ function read5(path) {
41787
41998
  safeDirectory3(dirname23(resolved));
41788
41999
  let fd;
41789
42000
  try {
41790
- fd = openSync17(resolved, constants15.O_RDONLY | constants15.O_NOFOLLOW | constants15.O_NONBLOCK);
42001
+ fd = openSync18(resolved, constants16.O_RDONLY | constants16.O_NOFOLLOW | constants16.O_NONBLOCK);
41791
42002
  } catch (error61) {
41792
42003
  if (error61.code === "ENOENT")
41793
42004
  return { resolved, raw: null, sha256: "absent" };
41794
42005
  throw new NativeRegistrationError("settings_unsafe");
41795
42006
  }
41796
42007
  try {
41797
- const s = fstatSync15(fd);
42008
+ const s = fstatSync17(fd);
41798
42009
  need9(s.isFile() && s.uid === process.getuid?.() && !(s.mode & 18) && s.nlink === 1 && s.size <= 1048576, "settings_unsafe");
41799
- const bytes = readFileSync25(fd);
42010
+ const bytes = readFileSync23(fd);
41800
42011
  need9(bytes.length <= 1048576, "settings_unsafe");
41801
42012
  const raw = bytes.toString("utf8");
41802
42013
  need9(Buffer.from(raw).equals(bytes), "settings_invalid");
41803
42014
  return { resolved, raw, sha256: sha7(bytes) };
41804
42015
  } finally {
41805
- closeSync17(fd);
42016
+ closeSync18(fd);
41806
42017
  }
41807
42018
  }
41808
42019
  function assertLosslessJson(raw) {
@@ -41860,7 +42071,7 @@ function validateSettings(raw) {
41860
42071
  need9(group && Array.isArray(group.hooks), "settings_invalid");
41861
42072
  for (const hook of group.hooks) {
41862
42073
  need9(hook && typeof hook === "object", "settings_invalid");
41863
- if (registrationName(hook.command) === "trash-guard") {
42074
+ if (["trash-guard", "trash-readiness"].includes(registrationName(hook.command) ?? "")) {
41864
42075
  need9((hook.async === undefined || hook.async === false) && hook.asyncRewake !== true, "registration_conflict");
41865
42076
  }
41866
42077
  }
@@ -41871,7 +42082,7 @@ function validateSettings(raw) {
41871
42082
  }
41872
42083
  function inspect2(options) {
41873
42084
  need9(options?.target === "codex" || options?.target === "claude", "input_invalid");
41874
- const home = realpathSync12(homedir9()), path = getSettingsPath("global", options.target);
42085
+ const home = realpathSync12(homedir10()), path = getSettingsPath("global", options.target);
41875
42086
  need9(isAbsolute20(path) && resolve19(path) === path, "input_invalid");
41876
42087
  const nativeHome = options.target === "codex" ? process.env.CODEX_HOME : process.env.CLAUDE_CONFIG_DIR;
41877
42088
  if (nativeHome !== undefined) {
@@ -41884,7 +42095,7 @@ function inspect2(options) {
41884
42095
  const parsed = validateSettings(before.raw);
41885
42096
  let preview2;
41886
42097
  try {
41887
- preview2 = previewNativeSafetyRegistration(options.target);
42098
+ preview2 = (options.readiness ? previewNativeReadinessRegistration : previewNativeSafetyRegistration)(options.target);
41888
42099
  } catch {
41889
42100
  throw new NativeRegistrationError("registration_conflict");
41890
42101
  }
@@ -41892,7 +42103,7 @@ function inspect2(options) {
41892
42103
  validateSettings(preview2.after);
41893
42104
  const changed = canonical6(parsed) !== canonical6(JSON.parse(preview2.after));
41894
42105
  const base = {
41895
- schema: "hasna.hooks.native-safety-registration/v1",
42106
+ schema: options.readiness ? "hasna.hooks.native-readiness-registration/v1" : "hasna.hooks.native-safety-registration/v1",
41896
42107
  target: options.target,
41897
42108
  settingsPath: path,
41898
42109
  resolvedPath: before.resolved,
@@ -41900,7 +42111,8 @@ function inspect2(options) {
41900
42111
  desiredSHA256: changed ? sha7(preview2.after) : before.sha256,
41901
42112
  commandSHA256: sha7(preview2.command),
41902
42113
  action: changed ? "register" : "unchanged",
41903
- guardVerified: true,
42114
+ guardVerified: !options.readiness,
42115
+ ...options.readiness ? { readinessRunnerVerified: true } : {},
41904
42116
  nativeAdoptionVerified: false
41905
42117
  };
41906
42118
  const plan = { ...base, planDigest: sha7(canonical6(base)) };
@@ -41908,37 +42120,40 @@ function inspect2(options) {
41908
42120
  return { home, before, preview: preview2, plan };
41909
42121
  }
41910
42122
  function syncDirectory2(path) {
41911
- const fd = openSync17(path, constants15.O_RDONLY | constants15.O_DIRECTORY | constants15.O_NOFOLLOW);
42123
+ const fd = openSync18(path, constants16.O_RDONLY | constants16.O_DIRECTORY | constants16.O_NOFOLLOW);
41912
42124
  try {
41913
42125
  fsyncSync7(fd);
41914
42126
  } finally {
41915
- closeSync17(fd);
42127
+ closeSync18(fd);
41916
42128
  }
41917
42129
  }
41918
42130
  function exclusive2(path, bytes) {
41919
- const fd = openSync17(path, constants15.O_CREAT | constants15.O_EXCL | constants15.O_WRONLY | constants15.O_NOFOLLOW, 384);
42131
+ const fd = openSync18(path, constants16.O_CREAT | constants16.O_EXCL | constants16.O_WRONLY | constants16.O_NOFOLLOW, 384);
41920
42132
  try {
41921
42133
  writeFileSync12(fd, bytes);
41922
42134
  fsyncSync7(fd);
41923
42135
  } finally {
41924
- closeSync17(fd);
42136
+ closeSync18(fd);
41925
42137
  }
41926
42138
  syncDirectory2(dirname23(path));
41927
42139
  }
41928
- async function planNativeSafetyRegistration(options, dependencies = {}) {
42140
+ async function planRegistration(options, dependencies = {}) {
41929
42141
  const selected = { ...options }, before = inspect2(selected);
41930
42142
  try {
41931
- await (dependencies.verify ?? verifyNativeSafetyCommand)(before.preview.command, before.home);
42143
+ if (selected.readiness)
42144
+ need9(nativeReadinessCommandVerified(before.preview.command), "guard_failed");
42145
+ else
42146
+ await (dependencies.verify ?? verifyNativeSafetyCommand)(before.preview.command, before.home);
41932
42147
  } catch {
41933
42148
  throw new NativeRegistrationError("guard_failed");
41934
42149
  }
41935
42150
  need9(inspect2(selected).plan.planDigest === before.plan.planDigest, "settings_changed");
41936
42151
  return before.plan;
41937
42152
  }
41938
- async function applyNativeSafetyRegistration(options, dependencies = {}) {
42153
+ async function applyRegistration(options, dependencies = {}) {
41939
42154
  const selected = { ...options };
41940
42155
  need9(/^[a-f0-9]{64}$/.test(selected.expectedPlanDigest), "input_invalid");
41941
- const plan = await planNativeSafetyRegistration(selected, dependencies);
42156
+ const plan = await planRegistration(selected, dependencies);
41942
42157
  need9(plan.planDigest === selected.expectedPlanDigest, "settings_changed");
41943
42158
  const before = inspect2(selected);
41944
42159
  need9(before.plan.planDigest === plan.planDigest, "settings_changed");
@@ -41946,8 +42161,8 @@ async function applyNativeSafetyRegistration(options, dependencies = {}) {
41946
42161
  return { ok: true, changed: false, ...plan };
41947
42162
  const operationId = randomUUID5();
41948
42163
  let directory2 = before.home;
41949
- for (const part of [".hasna", "hooks", "native", "trash-registration", operationId]) {
41950
- directory2 = join29(directory2, part);
42164
+ for (const part of [".hasna", "hooks", "native", selected.readiness ? "trash-readiness-registration" : "trash-registration", operationId]) {
42165
+ directory2 = join27(directory2, part);
41951
42166
  try {
41952
42167
  mkdirSync14(directory2, { mode: 448 });
41953
42168
  syncDirectory2(dirname23(directory2));
@@ -41958,12 +42173,12 @@ async function applyNativeSafetyRegistration(options, dependencies = {}) {
41958
42173
  safeDirectory3(directory2);
41959
42174
  }
41960
42175
  if (before.before.raw !== null)
41961
- exclusive2(join29(directory2, "settings.before"), before.before.raw);
41962
- exclusive2(join29(directory2, "intent.json"), JSON.stringify({ operationId, plan }) + `
42176
+ exclusive2(join27(directory2, "settings.before"), before.before.raw);
42177
+ exclusive2(join27(directory2, "intent.json"), JSON.stringify({ operationId, plan }) + `
41963
42178
  `);
41964
42179
  try {
41965
42180
  need9(inspect2(selected).plan.planDigest === plan.planDigest, "settings_changed");
41966
- const result = installHook("trash-guard", { target: selected.target, overwrite: true, expectedSettingsSha256: plan.beforeSHA256 });
42181
+ const result = selected.readiness ? installNativeReadinessRegistration(selected.target, plan.beforeSHA256) : installHook("trash-guard", { target: selected.target, overwrite: true, expectedSettingsSha256: plan.beforeSHA256 });
41967
42182
  need9(result.success, "apply_outcome_uncertain");
41968
42183
  const after = inspect2(selected);
41969
42184
  need9(after.before.sha256 === plan.desiredSHA256 && after.plan.resolvedPath === plan.resolvedPath && after.plan.action === "unchanged" && after.plan.commandSHA256 === plan.commandSHA256, "apply_outcome_uncertain");
@@ -41973,16 +42188,19 @@ async function applyNativeSafetyRegistration(options, dependencies = {}) {
41973
42188
  operationId,
41974
42189
  planDigest: plan.planDigest,
41975
42190
  settingsSHA256: after.before.sha256,
41976
- guardVerified: true,
42191
+ guardVerified: !selected.readiness,
42192
+ ...selected.readiness ? { readinessRunnerVerified: true } : {},
41977
42193
  nativeAdoptionVerified: false
41978
42194
  };
41979
- exclusive2(join29(directory2, "receipt.json"), JSON.stringify(receipt) + `
42195
+ exclusive2(join27(directory2, "receipt.json"), JSON.stringify(receipt) + `
41980
42196
  `);
41981
42197
  return receipt;
41982
42198
  } catch {
41983
42199
  throw new NativeRegistrationError("apply_outcome_uncertain", operationId);
41984
42200
  }
41985
42201
  }
42202
+ var planNativeSafetyRegistration = (options, dependencies = {}) => planRegistration({ target: options.target }, dependencies);
42203
+ var applyNativeSafetyRegistration = (options, dependencies = {}) => applyRegistration({ target: options.target, expectedPlanDigest: options.expectedPlanDigest }, dependencies);
41986
42204
 
41987
42205
  // src/index.ts
41988
42206
  function installHookForProject(name, options = {}) {
@@ -42005,11 +42223,11 @@ async function runHook(name, input2, options = {}) {
42005
42223
  throw new Error(`Hook '${name}' not found`);
42006
42224
  const hookScript = getHookPath(name) + "/src/hook.ts";
42007
42225
  const resolvedScript = custom3?.scriptPath;
42008
- const script = resolvedScript ?? (existsSync15(hookScript) ? hookScript : undefined);
42226
+ const script = resolvedScript ?? (existsSync16(hookScript) ? hookScript : undefined);
42009
42227
  if (!script)
42010
42228
  throw new Error(`Hook script not found: ${name}`);
42011
42229
  const { sha256Of: sha256Of2, checkScriptHash: checkScriptHash2 } = await Promise.resolve().then(() => (init_store(), exports_store));
42012
- const content = readFileSync26(script);
42230
+ const content = readFileSync25(script);
42013
42231
  const check2 = checkScriptHash2(name, sha256Of2(content));
42014
42232
  if (!check2.ok) {
42015
42233
  throw new Error(`Hook '${name}' script changed since it was trusted (sha256 ${check2.expected} != ${check2.actual}). Run 'hooks trust ${name}' to trust the new content.`);