@hasna/hooks 0.10.9 → 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/bin/hooks-mcp.js CHANGED
@@ -1130,9 +1130,9 @@ var init_migrations = __esm(() => {
1130
1130
  });
1131
1131
 
1132
1132
  // src/db/legacy-import.ts
1133
- import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
1134
- import { join as join6 } from "path";
1135
- import { homedir as homedir3 } from "os";
1133
+ import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
1134
+ import { join as join7 } from "path";
1135
+ import { homedir as homedir4 } from "os";
1136
1136
  function ensureMetaTable(db) {
1137
1137
  db.exec(`
1138
1138
  CREATE TABLE IF NOT EXISTS _meta (
@@ -1155,7 +1155,7 @@ function nanoid() {
1155
1155
  function importJsonlFile(db, filePath) {
1156
1156
  let count = 0;
1157
1157
  try {
1158
- const lines = readFileSync3(filePath, "utf-8").split(`
1158
+ const lines = readFileSync4(filePath, "utf-8").split(`
1159
1159
  `).filter(Boolean);
1160
1160
  for (const line of lines) {
1161
1161
  try {
@@ -1181,7 +1181,7 @@ function importJsonlFile(db, filePath) {
1181
1181
  function importErrorsLog(db, filePath) {
1182
1182
  let count = 0;
1183
1183
  try {
1184
- const lines = readFileSync3(filePath, "utf-8").split(`
1184
+ const lines = readFileSync4(filePath, "utf-8").split(`
1185
1185
  `).filter(Boolean);
1186
1186
  const linePattern = /^\[(.+?)\]\s+(?:\[session:(\S+)\]\s+)?(.+?)\s+\u2014\s+(.+)$/;
1187
1187
  for (const line of lines) {
@@ -1206,25 +1206,25 @@ function importErrorsLog(db, filePath) {
1206
1206
  } catch {}
1207
1207
  return count;
1208
1208
  }
1209
- function runLegacyImport(db, homeDir = homedir3()) {
1209
+ function runLegacyImport(db, homeDir = homedir4()) {
1210
1210
  try {
1211
1211
  if (isAlreadyDone(db))
1212
1212
  return;
1213
1213
  let total = 0;
1214
- const claudeProjectsDir = join6(homeDir, ".claude", "projects");
1215
- if (existsSync5(claudeProjectsDir)) {
1214
+ const claudeProjectsDir = join7(homeDir, ".claude", "projects");
1215
+ if (existsSync6(claudeProjectsDir)) {
1216
1216
  try {
1217
1217
  const projectDirs = readdirSync2(claudeProjectsDir);
1218
1218
  for (const dir of projectDirs) {
1219
- const projectDir = join6(claudeProjectsDir, dir);
1219
+ const projectDir = join7(claudeProjectsDir, dir);
1220
1220
  try {
1221
1221
  const files = readdirSync2(projectDir);
1222
1222
  for (const file of files) {
1223
1223
  if (file.match(/^session-log-\d{4}-\d{2}-\d{2}\.jsonl$/)) {
1224
- total += importJsonlFile(db, join6(projectDir, file));
1224
+ total += importJsonlFile(db, join7(projectDir, file));
1225
1225
  }
1226
1226
  if (file === "errors.log") {
1227
- total += importErrorsLog(db, join6(projectDir, file));
1227
+ total += importErrorsLog(db, join7(projectDir, file));
1228
1228
  }
1229
1229
  }
1230
1230
  } catch {}
@@ -1271,8 +1271,8 @@ __export(exports_db, {
1271
1271
  allowLocalStore: () => allowLocalStore
1272
1272
  });
1273
1273
  import { Database } from "bun:sqlite";
1274
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, cpSync } from "fs";
1275
- import { join as join7 } from "path";
1274
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, cpSync } from "fs";
1275
+ import { join as join8 } from "path";
1276
1276
  function refuseLocalStore(message) {
1277
1277
  localStoreRefusal = message;
1278
1278
  }
@@ -1287,8 +1287,8 @@ function localStoreRefusalMessage() {
1287
1287
  }
1288
1288
  function resolveDataDir() {
1289
1289
  const effective = getEffectiveDataRoot();
1290
- const oldDir = join7(getHomeDir(), ".hooks");
1291
- if (!existsSync6(effective) && existsSync6(oldDir)) {
1290
+ const oldDir = join8(getHomeDir(), ".hooks");
1291
+ if (!existsSync7(effective) && existsSync7(oldDir)) {
1292
1292
  mkdirSync2(effective, { recursive: true });
1293
1293
  cpSync(oldDir, effective, { recursive: true });
1294
1294
  }
@@ -1299,11 +1299,11 @@ function getDbPath() {
1299
1299
  if (explicitDb)
1300
1300
  return explicitDb;
1301
1301
  const dataDir2 = resolveDataDir();
1302
- return join7(dataDir2, "hooks.db");
1302
+ return join8(dataDir2, "hooks.db");
1303
1303
  }
1304
1304
  function ensureDir(dbPath) {
1305
1305
  const dir = dbPath.substring(0, dbPath.lastIndexOf("/"));
1306
- if (dir && !existsSync6(dir)) {
1306
+ if (dir && !existsSync7(dir)) {
1307
1307
  mkdirSync2(dir, { recursive: true });
1308
1308
  }
1309
1309
  }
@@ -1313,7 +1313,7 @@ function getDb() {
1313
1313
  if (instance)
1314
1314
  return instance;
1315
1315
  const dbPath = getDbPath();
1316
- const isNew = dbPath === ":memory:" || !existsSync6(dbPath);
1316
+ const isNew = dbPath === ":memory:" || !existsSync7(dbPath);
1317
1317
  ensureDir(dbPath);
1318
1318
  instance = new Database(dbPath);
1319
1319
  instance.exec("PRAGMA busy_timeout=5000");
@@ -1495,9 +1495,9 @@ function parseEnvFile(text3) {
1495
1495
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
1496
1496
  continue;
1497
1497
  let value = withoutExport.slice(equals + 1).trim();
1498
- const quote3 = value[0];
1499
- if (quote3 === '"' || quote3 === "'") {
1500
- if (value.length < 2 || !value.endsWith(quote3)) {
1498
+ const quote4 = value[0];
1499
+ if (quote4 === '"' || quote4 === "'") {
1500
+ if (value.length < 2 || !value.endsWith(quote4)) {
1501
1501
  unusable.add(key);
1502
1502
  continue;
1503
1503
  }
@@ -2230,11 +2230,11 @@ var init_local_opt_in = __esm(() => {
2230
2230
 
2231
2231
  // src/lib/hook-env.ts
2232
2232
  import { dirname as dirname11, isAbsolute as isAbsolute15 } from "path";
2233
- import { existsSync as existsSync12, realpathSync as realpathSync7, statSync } from "fs";
2233
+ import { existsSync as existsSync13, realpathSync as realpathSync7, statSync } from "fs";
2234
2234
  import { tmpdir } from "os";
2235
2235
  function isDeniedEnvName(name) {
2236
2236
  const upper = name.toUpperCase();
2237
- if (DENY_PREFIXES.some((prefix2) => upper.startsWith(prefix2)))
2237
+ if (DENY_PREFIXES.some((prefix3) => upper.startsWith(prefix3)))
2238
2238
  return true;
2239
2239
  if (DENY_SUFFIXES.some((suffix) => upper.endsWith(suffix)))
2240
2240
  return true;
@@ -2243,7 +2243,7 @@ function isDeniedEnvName(name) {
2243
2243
  function isInterpreterInjectionEnvName(name) {
2244
2244
  if (INTERPRETER_INJECTION.has(name))
2245
2245
  return true;
2246
- return INTERPRETER_INJECTION_PREFIXES.some((prefix2) => name.startsWith(prefix2));
2246
+ return INTERPRETER_INJECTION_PREFIXES.some((prefix3) => name.startsWith(prefix3));
2247
2247
  }
2248
2248
  function isBashFunctionEnvName(name) {
2249
2249
  return name.startsWith("BASH_FUNC_");
@@ -2278,7 +2278,7 @@ function isUnsafePathEntry(entry, home) {
2278
2278
  }
2279
2279
  function sanitizeHookPath(pathValue, home) {
2280
2280
  const baseline = PATH_BASELINE.slice();
2281
- if (process.platform === "darwin" && existsSync12("/opt/homebrew/bin"))
2281
+ if (process.platform === "darwin" && existsSync13("/opt/homebrew/bin"))
2282
2282
  baseline.push("/opt/homebrew/bin");
2283
2283
  const bunDir = dirname11(process.execPath);
2284
2284
  const seen = new Set;
@@ -2405,7 +2405,7 @@ __export(exports_run, {
2405
2405
  });
2406
2406
  import { randomBytes as randomBytes2 } from "crypto";
2407
2407
  import { rmSync as rmSync2, writeFileSync as writeFileSync10 } from "fs";
2408
- import { basename as basename5, dirname as dirname17, isAbsolute as isAbsolute17, join as join13 } from "path";
2408
+ import { basename as basename5, dirname as dirname17, isAbsolute as isAbsolute17, join as join12 } from "path";
2409
2409
  function extensionOf(scriptPath) {
2410
2410
  const base = basename5(scriptPath);
2411
2411
  const dot = base.lastIndexOf(".");
@@ -2476,7 +2476,7 @@ async function readPipeWithDeadline(stream, deadlineMs) {
2476
2476
  async function executeVerifiedScript(options) {
2477
2477
  const scriptDir = dirname17(options.scriptPath);
2478
2478
  const interpreter = interpreterFor(options.name, options.scriptPath, options.content);
2479
- const tempPath = join13(scriptDir, `.hooks-verified-${randomBytes2(12).toString("hex")}.${interpreter.tempExt}`);
2479
+ const tempPath = join12(scriptDir, `.hooks-verified-${randomBytes2(12).toString("hex")}.${interpreter.tempExt}`);
2480
2480
  let proc = null;
2481
2481
  let timedOut = false;
2482
2482
  const timeoutTimer = options.timeout ? setTimeout(() => {
@@ -2684,17 +2684,17 @@ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
2684
2684
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2685
2685
  import { z as z3 } from "zod";
2686
2686
  init_registry();
2687
- import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
2688
- import { join as join17, dirname as dirname19 } from "path";
2687
+ import { existsSync as existsSync14, readFileSync as readFileSync9 } from "fs";
2688
+ import { join as join13, dirname as dirname19 } from "path";
2689
2689
  import { fileURLToPath as fileURLToPath3 } from "url";
2690
2690
 
2691
2691
  // src/lib/installer.ts
2692
2692
  init_registry();
2693
2693
  init_resolve();
2694
2694
  init_manifest();
2695
- 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";
2695
+ 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";
2696
2696
  import { join as join10, dirname as dirname7, basename as basename3, resolve as resolve7 } from "path";
2697
- import { homedir as homedir5 } from "os";
2697
+ import { homedir as homedir6 } from "os";
2698
2698
  import { fileURLToPath as fileURLToPath2 } from "url";
2699
2699
 
2700
2700
  // src/lib/native-safety.ts
@@ -2831,6 +2831,159 @@ function installedNativeSafetyCommand(name) {
2831
2831
  return buildNativeSafetyCommand({ name, runtime, runtimeSHA256: pinEligibleFile(runtime, 200000000, true), worker: canonicalWorker, workerSHA256: pinEligibleFile(canonicalWorker, 256000), home, path: `${dirname3(runtime)}:${join5(home, ".bun/bin")}:/usr/local/bin:/usr/bin:/bin` });
2832
2832
  }
2833
2833
 
2834
+ // src/lib/native-readiness-command.ts
2835
+ import { createHash as createHash2 } from "crypto";
2836
+ import { constants as constants2, closeSync as closeSync2, existsSync as existsSync5, fstatSync as fstatSync2, lstatSync as lstatSync2, openSync as openSync2, readFileSync as readFileSync3, realpathSync as realpathSync3 } from "fs";
2837
+ import { dirname as dirname4, isAbsolute as isAbsolute3, join as join6 } from "path";
2838
+ import { homedir as homedir3 } from "os";
2839
+
2840
+ // src/lib/readiness-process.ts
2841
+ var supervisor = String.raw`
2842
+ const {spawn}=require("node:child_process");
2843
+ const spec=JSON.parse(process.argv.at(-1));
2844
+ // This runtime was created detached and is its group's live leader. Its
2845
+ // independent timer and parent pipe remain effective if the observer exits
2846
+ // or its event loop stops responding. Only this freshly created tree is hit.
2847
+ const cleanup=()=>{try{process.kill(-process.pid,"SIGKILL")}catch{process.exit(1)}};
2848
+ setTimeout(cleanup,spec.timeout);
2849
+ process.stdin.once("end",cleanup);
2850
+ process.stdin.once("error",cleanup);
2851
+ process.stdin.on("data",cleanup);
2852
+ process.stdin.resume();
2853
+ for(const signal of ["SIGTERM","SIGINT","SIGHUP"])process.once(signal,cleanup);
2854
+ let done=false, out=Buffer.alloc(0), err=Buffer.alloc(0);
2855
+ function report(status,error){
2856
+ if(done)return; done=true;
2857
+ process.stdout.write(JSON.stringify({status,error,stdout:out.toString("utf8"),stderr:err.toString("utf8")})+"\n");
2858
+ }
2859
+ const child=spawn(spec.command,spec.args,{stdio:["ignore","pipe","pipe"],env:process.env});
2860
+ for(const [stream,name] of [[child.stdout,"stdout"],[child.stderr,"stderr"]]){
2861
+ stream.on("data",bytes=>{
2862
+ if(done)return;
2863
+ const before=name==="stdout"?out:err;
2864
+ if(before.length+bytes.length>spec.maxBuffer){report(null,{code:"ENOBUFS"});return}
2865
+ if(name==="stdout")out=Buffer.concat([out,bytes]);else err=Buffer.concat([err,bytes]);
2866
+ });
2867
+ stream.on("error",()=>report(null,{code:"ECHILD"}));
2868
+ }
2869
+ child.on("error",()=>report(null,{code:"ECHILD"}));
2870
+ // A successful observation includes complete streams. A nested client that
2871
+ // keeps them open after its parent exits is a failure requiring group cleanup.
2872
+ child.on("exit",()=>setTimeout(()=>report(null,{code:"ECHILD"}),100));
2873
+ child.on("close",(status,signal)=>report(status,signal?{code:"ECHILD"}:undefined));
2874
+ `;
2875
+
2876
+ // src/native-readiness.ts
2877
+ function isReadinessProvider(value) {
2878
+ return value === "claude" || value === "codex" || value === "sumi";
2879
+ }
2880
+ var safeFailureCodes = new Set([
2881
+ "unsupported_platform",
2882
+ "station_detection_failed",
2883
+ "station_mismatch",
2884
+ "hosted_unavailable",
2885
+ "authentication_refused",
2886
+ "station_setup_required",
2887
+ "invalid_status",
2888
+ "deadline_exceeded",
2889
+ "retention_mismatch",
2890
+ "managed_instructions_refused",
2891
+ "removal_instruction_missing",
2892
+ "instructions_changed",
2893
+ "native_config_changed",
2894
+ "guard_refused",
2895
+ "owner_refused"
2896
+ ]);
2897
+
2898
+ // src/lib/native-readiness-command.ts
2899
+ var quote2 = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
2900
+ var bootstrap = String.raw`/* hasna-native-readiness-v1 */ (() => {
2901
+ const fs = require("node:fs"), path = require("node:path"), crypto = require("node:crypto");
2902
+ 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");
2903
+ try {
2904
+ const b = JSON.parse(Buffer.from(process.argv[1],"base64url").toString("utf8"));
2905
+ if (!["claude","codex","sumi"].includes(b.provider) || process.argv[2] !== b.provider || process.env.HOME !== b.home) throw Error();
2906
+ const check = (file, expected, maximum, executable) => {
2907
+ if (!path.isAbsolute(file) || fs.realpathSync(file) !== file) throw Error();
2908
+ for (let p = path.dirname(file);; p = path.dirname(p)) {
2909
+ const s = fs.lstatSync(p);
2910
+ if (!s.isDirectory() || ![0,process.getuid()].includes(s.uid) || s.mode & 0o022) throw Error();
2911
+ if (p === path.dirname(p)) break;
2912
+ }
2913
+ const fd = fs.openSync(file,fs.constants.O_RDONLY|fs.constants.O_NOFOLLOW|fs.constants.O_NONBLOCK);
2914
+ try {
2915
+ const s = fs.fstatSync(fd);
2916
+ if (!s.isFile() || ![0,process.getuid()].includes(s.uid) || s.mode & 0o022 || s.size > maximum || executable && !(s.mode & 0o100)) throw Error();
2917
+ const bytes = Buffer.alloc(s.size+1); let length=0;
2918
+ while (length<bytes.length) { const n=fs.readSync(fd,bytes,length,bytes.length-length,null); if(!n)break; length+=n; }
2919
+ const after=fs.fstatSync(fd);
2920
+ if(length!==s.size || after.size!==s.size || after.mtimeMs!==s.mtimeMs || after.ctimeMs!==s.ctimeMs)throw Error();
2921
+ const content=bytes.subarray(0,length);
2922
+ if(crypto.createHash("sha256").update(content).digest("hex")!==expected)throw Error();
2923
+ return content;
2924
+ } finally { fs.closeSync(fd); }
2925
+ };
2926
+ check(b.runtime,b.runtimeSHA256,200000000,true);
2927
+ const bytes=check(b.worker,b.workerSHA256,256000,false);
2928
+ new Function("require","process",bytes.toString("utf8"))(require,process);
2929
+ } catch { failed(); }
2930
+ })();`;
2931
+ var prefix2 = "/usr/bin/env -i ";
2932
+ function valid(b) {
2933
+ 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)))
2934
+ throw new Error("Invalid readiness command binding");
2935
+ }
2936
+ function buildNativeReadinessCommand(binding) {
2937
+ valid(binding);
2938
+ const encoded = Buffer.from(JSON.stringify(binding)).toString("base64url");
2939
+ 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(" ");
2940
+ }
2941
+ function nativeReadinessCommandBinding(command) {
2942
+ if (!command.startsWith(prefix2) || !command.includes(quote2(bootstrap)))
2943
+ return;
2944
+ const tail = command.slice(command.indexOf(quote2(bootstrap)) + quote2(bootstrap).length);
2945
+ const match = /^ '([A-Za-z0-9_-]{1,16384})' '(claude|codex|sumi)'$/.exec(tail);
2946
+ if (!match)
2947
+ return;
2948
+ try {
2949
+ const b = JSON.parse(Buffer.from(match[1], "base64url").toString("utf8"));
2950
+ return buildNativeReadinessCommand(b) === command ? b : undefined;
2951
+ } catch {
2952
+ return;
2953
+ }
2954
+ }
2955
+ function pin(path, maximum, executable) {
2956
+ if (realpathSync3(path) !== path)
2957
+ throw new Error("Readiness runner requires canonical files");
2958
+ for (let p = dirname4(path);; p = dirname4(p)) {
2959
+ const s = lstatSync2(p);
2960
+ if (!s.isDirectory() || ![0, process.getuid?.()].includes(s.uid) || s.mode & 18)
2961
+ throw new Error("Readiness runner requires safe ancestry");
2962
+ if (p === dirname4(p))
2963
+ break;
2964
+ }
2965
+ const fd = openSync2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
2966
+ try {
2967
+ const s = fstatSync2(fd);
2968
+ if (!s.isFile() || ![0, process.getuid?.()].includes(s.uid) || s.mode & 18 || s.size > maximum || executable && !(s.mode & 64))
2969
+ throw new Error("Readiness runner requires safe regular files");
2970
+ const bytes = readFileSync3(fd);
2971
+ if (bytes.length > maximum)
2972
+ throw new Error("Readiness runner is too large");
2973
+ return createHash2("sha256").update(bytes).digest("hex");
2974
+ } finally {
2975
+ closeSync2(fd);
2976
+ }
2977
+ }
2978
+ function installedNativeReadinessCommand(provider) {
2979
+ const candidates = [join6(import.meta.dir, "../../bin/native-readiness-entry.js"), join6(import.meta.dir, "../bin/native-readiness-entry.js"), join6(import.meta.dir, "native-readiness-entry.js")];
2980
+ const selected = candidates.find(existsSync5);
2981
+ if (!selected)
2982
+ throw new Error("Build or install the native readiness runner first");
2983
+ const runtime = realpathSync3(process.execPath), worker = realpathSync3(selected);
2984
+ return buildNativeReadinessCommand({ provider, home: realpathSync3(homedir3()), runtime, runtimeSHA256: pin(runtime, 200000000, true), worker, workerSHA256: pin(worker, 256000, false) });
2985
+ }
2986
+
2834
2987
  // src/lib/registration.ts
2835
2988
  init_resolve();
2836
2989
 
@@ -2898,6 +3051,8 @@ function registrationName(command) {
2898
3051
  const native = nativeSafetyRegistration(command);
2899
3052
  if (native)
2900
3053
  return native;
3054
+ if (nativeReadinessCommandBinding(command))
3055
+ return "trash-readiness";
2901
3056
  if (/^hooks run mementos-context(?:\s|$)/.test(command.trim())) {
2902
3057
  try {
2903
3058
  retainedMementosRegistration([command.trim()]);
@@ -2964,13 +3119,13 @@ function hookRegisteredInSettings(settings, name, eventSpec, hookMatcher) {
2964
3119
  }
2965
3120
 
2966
3121
  // src/lib/store.ts
2967
- import { createHash as createHash2 } from "crypto";
2968
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync } from "fs";
3122
+ import { createHash as createHash3 } from "crypto";
3123
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2, renameSync } from "fs";
2969
3124
  init_db();
2970
3125
  init_config();
2971
3126
  import { randomBytes } from "crypto";
2972
3127
  function sha256Of(input) {
2973
- return createHash2("sha256").update(input).digest("hex");
3128
+ return createHash3("sha256").update(input).digest("hex");
2974
3129
  }
2975
3130
  function storeDb() {
2976
3131
  return isLocalStoreRefused() ? null : getDb();
@@ -3026,11 +3181,11 @@ function assertLockShape(parsed, path) {
3026
3181
  }
3027
3182
  function readLock() {
3028
3183
  const path = getLockPath();
3029
- if (!existsSync7(path))
3184
+ if (!existsSync8(path))
3030
3185
  return { hooks: {} };
3031
3186
  let raw;
3032
3187
  try {
3033
- raw = readFileSync4(path, "utf-8");
3188
+ raw = readFileSync5(path, "utf-8");
3034
3189
  } catch {
3035
3190
  throw new LockFileError(`hooks.lock (${path}) could not be read. Repair: fix permissions or move the file aside and run 'hooks sync'.`);
3036
3191
  }
@@ -3067,15 +3222,15 @@ function getPinnedHook(name) {
3067
3222
  function checkScriptHash(name, actual) {
3068
3223
  const db = storeDb();
3069
3224
  const record = db ? getHookRecord(db, name) : null;
3070
- const pin = getPinnedHook(name);
3071
- const expected = record?.sha256 ?? pin?.sha256 ?? null;
3225
+ const pin2 = getPinnedHook(name);
3226
+ const expected = record?.sha256 ?? pin2?.sha256 ?? null;
3072
3227
  if (expected !== null && expected !== actual) {
3073
3228
  return { ok: false, pinned: true, expected, actual, name };
3074
3229
  }
3075
3230
  if (expected === null) {
3076
3231
  const now2 = new Date().toISOString();
3077
- const version = pin?.version ?? record?.version ?? "0.0.0";
3078
- const source = pin?.source ?? record?.source_type ?? "local";
3232
+ const version = pin2?.version ?? record?.version ?? "0.0.0";
3233
+ const source = pin2?.source ?? record?.source_type ?? "local";
3079
3234
  if (db) {
3080
3235
  upsertHookRecord(db, {
3081
3236
  name,
@@ -3092,9 +3247,9 @@ function checkScriptHash(name, actual) {
3092
3247
  if (db) {
3093
3248
  upsertHookRecord(db, {
3094
3249
  name,
3095
- version: record?.version ?? pin?.version ?? "0.0.0",
3250
+ version: record?.version ?? pin2?.version ?? "0.0.0",
3096
3251
  sha256: expected,
3097
- source_type: record?.source_type ?? pin?.source ?? "local",
3252
+ source_type: record?.source_type ?? pin2?.source ?? "local",
3098
3253
  last_verified_at: now
3099
3254
  });
3100
3255
  }
@@ -3102,16 +3257,16 @@ function checkScriptHash(name, actual) {
3102
3257
  }
3103
3258
 
3104
3259
  // src/lib/codex-settings.ts
3105
- 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";
3106
- import { dirname as dirname4 } from "path";
3107
- import { createHash as createHash3, randomUUID } from "crypto";
3260
+ 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";
3261
+ import { dirname as dirname5 } from "path";
3262
+ import { createHash as createHash4, randomUUID } from "crypto";
3108
3263
  var snapshots = new WeakMap;
3109
3264
  function current(path) {
3110
3265
  try {
3111
- const stat = lstatSync2(path);
3266
+ const stat = lstatSync3(path);
3112
3267
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1048576)
3113
3268
  throw new Error("invalid_file");
3114
- return readFileSync5(path, "utf8");
3269
+ return readFileSync6(path, "utf8");
3115
3270
  } catch (error) {
3116
3271
  if (error.code === "ENOENT")
3117
3272
  return null;
@@ -3142,7 +3297,7 @@ function readCodexSettings(path) {
3142
3297
  function assertSettingsPreimage(raw, expected) {
3143
3298
  if (expected === undefined)
3144
3299
  return;
3145
- if (!/^(?:absent|[a-f0-9]{64})$/.test(expected) || (raw === null ? "absent" : createHash3("sha256").update(raw).digest("hex")) !== expected) {
3300
+ if (!/^(?:absent|[a-f0-9]{64})$/.test(expected) || (raw === null ? "absent" : createHash4("sha256").update(raw).digest("hex")) !== expected) {
3146
3301
  throw new Error("Hook settings changed since planning; no settings were written.");
3147
3302
  }
3148
3303
  }
@@ -3156,11 +3311,11 @@ function writeCodexSettings(path, settings, expectedSha256) {
3156
3311
  ` });
3157
3312
  }
3158
3313
  function writeHookSettingsPreimage(path, settings, before) {
3159
- mkdirSync4(dirname4(path), { recursive: true, mode: 448 });
3314
+ mkdirSync4(dirname5(path), { recursive: true, mode: 448 });
3160
3315
  const lock = `${path}.hooks-write.lock`;
3161
3316
  let fd;
3162
3317
  try {
3163
- fd = openSync2(lock, "wx", 384);
3318
+ fd = openSync3(lock, "wx", 384);
3164
3319
  } catch {
3165
3320
  throw new Error("Codex settings are being edited; retry after the active writer finishes.");
3166
3321
  }
@@ -3175,23 +3330,23 @@ function writeHookSettingsPreimage(path, settings, before) {
3175
3330
  throw new Error("Hook settings changed since reading; re-read before retrying.");
3176
3331
  renameSync2(temp, path);
3177
3332
  } finally {
3178
- if (existsSync8(temp))
3333
+ if (existsSync9(temp))
3179
3334
  unlinkSync(temp);
3180
- closeSync2(fd);
3335
+ closeSync3(fd);
3181
3336
  unlinkSync(lock);
3182
3337
  }
3183
3338
  }
3184
3339
 
3185
3340
  // src/lib/claude-skills-coordination.ts
3186
- import { createHash as createHash5 } from "crypto";
3341
+ import { createHash as createHash6 } from "crypto";
3187
3342
 
3188
3343
  // ../../node_modules/.bun/@hasna+skills@0.9.17+a00d3064559565bb/node_modules/@hasna/skills/dist/index.js
3189
- import { existsSync as existsSync9 } from "fs";
3190
- import { homedir as homedir4 } from "os";
3191
- import { join as join8, resolve as resolve3 } from "path";
3344
+ import { existsSync as existsSync10 } from "fs";
3345
+ import { homedir as homedir5 } from "os";
3346
+ import { join as join9, resolve as resolve3 } from "path";
3192
3347
  import { homedir as pathsResolverHomedir2 } from "os";
3193
3348
  import { join as pathsResolverJoin2 } from "path";
3194
- import { createHash as createHash4 } from "crypto";
3349
+ import { createHash as createHash5 } from "crypto";
3195
3350
  import { existsSync as existsSync52, readdirSync as readdirSync3, readFileSync as readFileSync42, statSync as statSync3 } from "fs";
3196
3351
  import { join as join52, sep as sep2 } from "path";
3197
3352
  import { basename as basename2, isAbsolute as isAbsolute22, resolve as resolve22 } from "path";
@@ -3199,23 +3354,23 @@ import { createHash as createHash16 } from "crypto";
3199
3354
  import { lstatSync as lstatSync20, readFileSync as readFileSync16, readdirSync as readdirSync9, realpathSync as realpathSync4 } from "fs";
3200
3355
  import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute14, join as join26, resolve as resolve14, sep as sep5 } from "path";
3201
3356
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
3202
- import { dirname as dirname32, isAbsolute as isAbsolute3, join as join72, resolve as resolve32 } from "path";
3203
- import { lstatSync as lstatSync3, readFileSync as readFileSync6, opendirSync } from "fs";
3357
+ import { dirname as dirname32, isAbsolute as isAbsolute32, join as join72, resolve as resolve32 } from "path";
3358
+ import { lstatSync as lstatSync32, readFileSync as readFileSync62, opendirSync } from "fs";
3204
3359
  import { createHash as createHash22 } from "crypto";
3205
3360
  import { lstatSync as lstatSync4, opendirSync as opendirSync2 } from "fs";
3206
3361
  import { dirname as dirname42, isAbsolute as isAbsolute4, join as join82, resolve as resolve4 } from "path";
3207
3362
  import { createHash as createHash32 } from "crypto";
3208
- import { closeSync as closeSync3, constants as constants2, fstatSync as fstatSync2, lstatSync as lstatSync5, openSync as openSync3, readSync } from "fs";
3209
- import { dirname as dirname5, isAbsolute as isAbsolute5, resolve as resolve5 } from "path";
3363
+ import { closeSync as closeSync4, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync5, openSync as openSync4, readSync } from "fs";
3364
+ import { dirname as dirname52, isAbsolute as isAbsolute5, resolve as resolve5 } from "path";
3210
3365
  import { createHash as createHash42 } from "crypto";
3211
3366
  import { lstatSync as lstatSync6, readlinkSync } from "fs";
3212
- import { dirname as dirname6, isAbsolute as isAbsolute6, join as join9, parse, resolve as resolve6, sep as sep22 } from "path";
3367
+ import { dirname as dirname6, isAbsolute as isAbsolute6, join as join92, parse, resolve as resolve6, sep as sep22 } from "path";
3213
3368
  import { closeSync as closeSync12, constants as constants9, fstatSync as fstatSync12, lstatSync as lstatSync17, openSync as openSync12, opendirSync as opendirSync5, readSync as readSync8 } from "fs";
3214
3369
  import { basename as basename6, dirname as dirname13, isAbsolute as isAbsolute12, join as join25, resolve as resolve12 } from "path";
3215
3370
  import { isAbsolute as isAbsolute11, join as join23, resolve as resolve11 } from "path";
3216
3371
  import { createRequire } from "module";
3217
3372
  import { createHash as createHash10, randomUUID as randomUUID2 } from "crypto";
3218
- 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";
3373
+ 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";
3219
3374
  import { dirname as dirname10, join as join16, resolve as resolve9, relative as relative4, isAbsolute as isAbsolute9, sep as sep4 } from "path";
3220
3375
  import { homedir as homedir22 } from "os";
3221
3376
  import { closeSync as closeSync8, constants as constants5, fstatSync as fstatSync8, lstatSync as lstatSync11, openSync as openSync8, readSync as readSync4 } from "fs";
@@ -3516,8 +3671,8 @@ var require_directives = __commonJS((exports) => {
3516
3671
  if (parts.length < 2)
3517
3672
  return false;
3518
3673
  }
3519
- const [handle, prefix2] = parts;
3520
- this.tags[handle] = prefix2;
3674
+ const [handle, prefix3] = parts;
3675
+ this.tags[handle] = prefix3;
3521
3676
  return true;
3522
3677
  }
3523
3678
  case "%YAML": {
@@ -3561,10 +3716,10 @@ var require_directives = __commonJS((exports) => {
3561
3716
  const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s);
3562
3717
  if (!suffix)
3563
3718
  onError(`The ${source} tag has no suffix`);
3564
- const prefix2 = this.tags[handle];
3565
- if (prefix2) {
3719
+ const prefix3 = this.tags[handle];
3720
+ if (prefix3) {
3566
3721
  try {
3567
- return prefix2 + decodeURIComponent(suffix);
3722
+ return prefix3 + decodeURIComponent(suffix);
3568
3723
  } catch (error) {
3569
3724
  onError(String(error));
3570
3725
  return null;
@@ -3576,9 +3731,9 @@ var require_directives = __commonJS((exports) => {
3576
3731
  return null;
3577
3732
  }
3578
3733
  tagString(tag) {
3579
- for (const [handle, prefix2] of Object.entries(this.tags)) {
3580
- if (tag.startsWith(prefix2))
3581
- return handle + escapeTagName(tag.substring(prefix2.length));
3734
+ for (const [handle, prefix3] of Object.entries(this.tags)) {
3735
+ if (tag.startsWith(prefix3))
3736
+ return handle + escapeTagName(tag.substring(prefix3.length));
3582
3737
  }
3583
3738
  return tag[0] === "!" ? tag : `!<${tag}>`;
3584
3739
  }
@@ -3595,11 +3750,11 @@ var require_directives = __commonJS((exports) => {
3595
3750
  tagNames = Object.keys(tags);
3596
3751
  } else
3597
3752
  tagNames = [];
3598
- for (const [handle, prefix2] of tagEntries) {
3599
- if (handle === "!!" && prefix2 === "tag:yaml.org,2002:")
3753
+ for (const [handle, prefix3] of tagEntries) {
3754
+ if (handle === "!!" && prefix3 === "tag:yaml.org,2002:")
3600
3755
  continue;
3601
- if (!doc || tagNames.some((tn) => tn.startsWith(prefix2)))
3602
- lines.push(`%TAG ${handle} ${prefix2}`);
3756
+ if (!doc || tagNames.some((tn) => tn.startsWith(prefix3)))
3757
+ lines.push(`%TAG ${handle} ${prefix3}`);
3603
3758
  }
3604
3759
  return lines.join(`
3605
3760
  `);
@@ -3630,14 +3785,14 @@ var require_anchors = __commonJS((exports) => {
3630
3785
  });
3631
3786
  return anchors;
3632
3787
  }
3633
- function findNewAnchor(prefix2, exclude) {
3788
+ function findNewAnchor(prefix3, exclude) {
3634
3789
  for (let i = 1;; ++i) {
3635
- const name = `${prefix2}${i}`;
3790
+ const name = `${prefix3}${i}`;
3636
3791
  if (!exclude.has(name))
3637
3792
  return name;
3638
3793
  }
3639
3794
  }
3640
- function createNodeAnchors(doc, prefix2) {
3795
+ function createNodeAnchors(doc, prefix3) {
3641
3796
  const aliasObjects = [];
3642
3797
  const sourceObjects = new Map;
3643
3798
  let prevAnchors = null;
@@ -3645,7 +3800,7 @@ var require_anchors = __commonJS((exports) => {
3645
3800
  onAnchor: (source) => {
3646
3801
  aliasObjects.push(source);
3647
3802
  prevAnchors ?? (prevAnchors = anchorNames(doc));
3648
- const anchor = findNewAnchor(prefix2, prevAnchors);
3803
+ const anchor = findNewAnchor(prefix3, prevAnchors);
3649
3804
  prevAnchors.add(anchor);
3650
3805
  return anchor;
3651
3806
  },
@@ -5470,10 +5625,10 @@ var require_int = __commonJS((exports) => {
5470
5625
  var stringifyNumber = require_stringifyNumber();
5471
5626
  var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value);
5472
5627
  var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix);
5473
- function intStringify(node, radix, prefix2) {
5628
+ function intStringify(node, radix, prefix3) {
5474
5629
  const { value } = node;
5475
5630
  if (intIdentify(value) && value >= 0)
5476
- return prefix2 + value.toString(radix);
5631
+ return prefix3 + value.toString(radix);
5477
5632
  return stringifyNumber.stringifyNumber(node);
5478
5633
  }
5479
5634
  var intOct = {
@@ -5884,11 +6039,11 @@ var require_int2 = __commonJS((exports) => {
5884
6039
  const n = parseInt(str, radix);
5885
6040
  return sign === "-" ? -1 * n : n;
5886
6041
  }
5887
- function intStringify(node, radix, prefix2) {
6042
+ function intStringify(node, radix, prefix3) {
5888
6043
  const { value } = node;
5889
6044
  if (intIdentify(value)) {
5890
6045
  const str = value.toString(radix);
5891
- return value < 0 ? "-" + prefix2 + str.substr(1) : prefix2 + str;
6046
+ return value < 0 ? "-" + prefix3 + str.substr(1) : prefix3 + str;
5892
6047
  }
5893
6048
  return stringifyNumber.stringifyNumber(node);
5894
6049
  }
@@ -8802,9 +8957,9 @@ var require_lexer = __commonJS((exports) => {
8802
8957
  }
8803
8958
  }
8804
8959
  *parseQuotedScalar() {
8805
- const quote2 = this.charAt(0);
8806
- let end = this.buffer.indexOf(quote2, this.pos + 1);
8807
- if (quote2 === "'") {
8960
+ const quote3 = this.charAt(0);
8961
+ let end = this.buffer.indexOf(quote3, this.pos + 1);
8962
+ if (quote3 === "'") {
8808
8963
  while (end !== -1 && this.buffer[end + 1] === "'")
8809
8964
  end = this.buffer.indexOf("'", end + 2);
8810
8965
  } else {
@@ -10089,10 +10244,10 @@ var SKILLS_HOME_ENV = "SKILLS_HOME";
10089
10244
  var DEFAULT_SQLITE_FILENAME = "server.db";
10090
10245
  var GLOBAL_CONFIG_FILENAME = "config.json";
10091
10246
  function effectiveHome() {
10092
- return process.env["HOME"] || process.env["USERPROFILE"] || homedir4() || "/tmp";
10247
+ return process.env["HOME"] || process.env["USERPROFILE"] || homedir5() || "/tmp";
10093
10248
  }
10094
10249
  function legacyDataRoot() {
10095
- return join8(effectiveHome(), ".hasna", "skills");
10250
+ return join9(effectiveHome(), ".hasna", "skills");
10096
10251
  }
10097
10252
  function resolverDataRoot(home = effectiveHome(), env) {
10098
10253
  return dataDir2({ app: "skills", home, env });
@@ -10101,7 +10256,7 @@ function adoptResolverDataRoot2(resolved, env = process.env) {
10101
10256
  const dataOverride = env.HASNA_DATA_HOME;
10102
10257
  if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
10103
10258
  return true;
10104
- return existsSync9(join8(resolved, DEFAULT_SQLITE_FILENAME)) || existsSync9(join8(resolved, GLOBAL_CONFIG_FILENAME));
10259
+ return existsSync10(join9(resolved, DEFAULT_SQLITE_FILENAME)) || existsSync10(join9(resolved, GLOBAL_CONFIG_FILENAME));
10105
10260
  }
10106
10261
  function exactDataRoot() {
10107
10262
  for (const key of [DATA_DIR_ENV, HASNA_SKILLS_HOME_ENV, SKILLS_HOME_ENV]) {
@@ -10335,7 +10490,7 @@ function* bundleHashParts(files) {
10335
10490
  yield new TextEncoder().encode("\x00");
10336
10491
  }
10337
10492
  function hashBundleFiles(files) {
10338
- const hash = createHash4(CONTENT_HASH_ALGORITHM);
10493
+ const hash = createHash5(CONTENT_HASH_ALGORITHM);
10339
10494
  for (const part of bundleHashParts(files))
10340
10495
  hash.update(part);
10341
10496
  return hash.digest("hex");
@@ -10790,10 +10945,10 @@ function quoted(value) {
10790
10945
  throw new Error("Invalid Hermes Skills command");
10791
10946
  return `'${value.replace(/'/g, `'\\''`)}'`;
10792
10947
  }
10793
- function hermesHookDefinitions(supervisor) {
10794
- if (!isAbsolute3(supervisor.runtime) || !isAbsolute3(supervisor.path))
10948
+ function hermesHookDefinitions(supervisor2) {
10949
+ if (!isAbsolute32(supervisor2.runtime) || !isAbsolute32(supervisor2.path))
10795
10950
  throw new Error("Expected absolute Hermes supervisor paths");
10796
- const entry = (event) => ({ command: `${quoted(supervisor.runtime)} ${quoted(supervisor.path)} --event ${event}`, timeout: 15, ...event === "pre_tool_call" ? { fail_closed: true } : {} });
10951
+ const entry = (event) => ({ command: `${quoted(supervisor2.runtime)} ${quoted(supervisor2.path)} --event ${event}`, timeout: 15, ...event === "pre_tool_call" ? { fail_closed: true } : {} });
10797
10952
  return { pre_llm_call: entry("pre_llm_call"), pre_tool_call: entry("pre_tool_call") };
10798
10953
  }
10799
10954
  function assertHermesEnvironment(home) {
@@ -10804,30 +10959,30 @@ function assertHermesEnvironment(home) {
10804
10959
  throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
10805
10960
  }
10806
10961
  const selected = process.env.HERMES_HOME?.trim();
10807
- if (selected && (!isAbsolute3(selected) || resolve32(selected) !== join72(resolve32(home), ".hermes")))
10962
+ if (selected && (!isAbsolute32(selected) || resolve32(selected) !== join72(resolve32(home), ".hermes")))
10808
10963
  throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
10809
10964
  if (process.env.HERMES_ENABLE_PROJECT_PLUGINS?.trim() && !["0", "false", "no", "off"].includes(process.env.HERMES_ENABLE_PROJECT_PLUGINS.trim().toLowerCase()))
10810
10965
  throw new Error("NATIVE_SKILL_DRIFT: Hermes project plugins require a dedicated discovery review");
10811
10966
  }
10812
10967
  function read(path) {
10813
10968
  for (let current2 = path;; current2 = dirname32(current2)) {
10814
- if (lstatSync3(current2, { throwIfNoEntry: false })?.isSymbolicLink())
10969
+ if (lstatSync32(current2, { throwIfNoEntry: false })?.isSymbolicLink())
10815
10970
  throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes protection input");
10816
10971
  if (dirname32(current2) === current2)
10817
10972
  break;
10818
10973
  }
10819
- const stat = lstatSync3(path, { throwIfNoEntry: false });
10974
+ const stat = lstatSync32(path, { throwIfNoEntry: false });
10820
10975
  if (!stat)
10821
10976
  return null;
10822
10977
  if (!stat.isFile() || stat.size > 1024 * 1024)
10823
10978
  throw new Error("NATIVE_SKILL_DRIFT: invalid Hermes protection input");
10824
- return readFileSync6(path, "utf8");
10979
+ return readFileSync62(path, "utf8");
10825
10980
  }
10826
- function assertHermesProtection(home, config, command, profile, supervisor) {
10981
+ function assertHermesProtection(home, config, command, profile, supervisor2) {
10827
10982
  assertHermesEnvironment(home);
10828
10983
  if (read(join72(home, ".hermes", HERMES_OPT_OUT)) === null)
10829
10984
  throw new Error("NATIVE_SKILL_DRIFT: Hermes bundled skill reseeding is not disabled; run skills hook install");
10830
- if (!supervisor || !isAbsolute3(supervisor.path) || supervisor.runtime !== process.execPath || read(supervisor.path) !== renderHermesSupervisor(command, profile))
10985
+ if (!supervisor2 || !isAbsolute32(supervisor2.path) || supervisor2.runtime !== process.execPath || read(supervisor2.path) !== renderHermesSupervisor(command, profile))
10831
10986
  throw new Error("NATIVE_SKILL_DRIFT: Hermes owned supervisor changed; run skills hook install");
10832
10987
  const raw = read(join72(home, ".hermes/shell-hooks-allowlist.json"));
10833
10988
  let trust;
@@ -10836,7 +10991,7 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
10836
10991
  } catch {
10837
10992
  throw new Error("NATIVE_SKILL_DRIFT: invalid Hermes native hook trust");
10838
10993
  }
10839
- for (const [event, entry] of Object.entries(hermesHookDefinitions(supervisor))) {
10994
+ for (const [event, entry] of Object.entries(hermesHookDefinitions(supervisor2))) {
10840
10995
  if (!Array.isArray(config.hooks?.[event]) || config.hooks[event].filter((actual) => sameEntry(actual, entry)).length !== 1)
10841
10996
  throw new Error("NATIVE_SKILL_DRIFT: Hermes required native hooks changed; run skills hook install");
10842
10997
  if (!Array.isArray(trust?.approvals) || !trust.approvals.some((approval) => object2(approval) && approval.event === event && approval.command === entry.command))
@@ -10854,7 +11009,7 @@ function assertHermesProtection(home, config, command, profile, supervisor) {
10854
11009
  function assertNoHermesLegacyShadow(root) {
10855
11010
  let entries = 0;
10856
11011
  for (let cursor = resolve32(root);; cursor = dirname32(cursor)) {
10857
- if (lstatSync3(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
11012
+ if (lstatSync32(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
10858
11013
  throw new Error("NATIVE_SKILL_DRIFT: symlink Hermes legacy discovery root");
10859
11014
  if (dirname32(cursor) === cursor)
10860
11015
  break;
@@ -10862,7 +11017,7 @@ function assertNoHermesLegacyShadow(root) {
10862
11017
  function scan(path, depth) {
10863
11018
  if (++entries > 1e4 || depth > 64)
10864
11019
  throw new Error("NATIVE_SKILL_DRIFT: Hermes legacy discovery limit exceeded; review the root before continuing");
10865
- const stat = lstatSync3(path, { throwIfNoEntry: false });
11020
+ const stat = lstatSync32(path, { throwIfNoEntry: false });
10866
11021
  if (!stat)
10867
11022
  return;
10868
11023
  if (stat.isSymbolicLink())
@@ -10872,9 +11027,9 @@ function assertNoHermesLegacyShadow(root) {
10872
11027
  throw new Error("NATIVE_SKILL_DRIFT: unsupported Hermes legacy discovery input");
10873
11028
  return;
10874
11029
  }
10875
- if (lstatSync3(join72(path, "skills-cli.md"), { throwIfNoEntry: false }))
11030
+ if (lstatSync32(join72(path, "skills-cli.md"), { throwIfNoEntry: false }))
10876
11031
  throw new Error("NATIVE_SKILL_DRIFT: legacy Hermes skills-cli.md can shadow the owned bridge; preserve and retire it before continuing");
10877
- const hasDescriptor = Boolean(lstatSync3(join72(path, "SKILL.md"), { throwIfNoEntry: false }));
11032
+ const hasDescriptor = Boolean(lstatSync32(join72(path, "SKILL.md"), { throwIfNoEntry: false }));
10878
11033
  const directory = opendirSync(path);
10879
11034
  try {
10880
11035
  for (let child = directory.readSync();child; child = directory.readSync()) {
@@ -11007,10 +11162,10 @@ function discoveryByteBudget() {
11007
11162
  function safe2(path) {
11008
11163
  if (typeof path !== "string" || !isAbsolute5(path) || path !== resolve5(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
11009
11164
  throw new Error("Expected a canonical absolute raw discovery path");
11010
- for (let cursor = path;; cursor = dirname5(cursor)) {
11165
+ for (let cursor = path;; cursor = dirname52(cursor)) {
11011
11166
  if (lstatSync5(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
11012
11167
  throw new Error("Refusing symlink raw discovery input");
11013
- if (dirname5(cursor) === cursor)
11168
+ if (dirname52(cursor) === cursor)
11014
11169
  break;
11015
11170
  }
11016
11171
  }
@@ -11038,9 +11193,9 @@ function hashRawDiscoveryFile(path, budget, changes) {
11038
11193
  throw new Error("Unsupported or oversized raw discovery input");
11039
11194
  if (initial.size > budget.remaining)
11040
11195
  throw new Error("Raw discovery aggregate byte limit exceeded");
11041
- const fd = openSync3(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
11196
+ const fd = openSync4(path, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
11042
11197
  try {
11043
- const opened = fstatSync2(fd);
11198
+ const opened = fstatSync3(fd);
11044
11199
  if (!opened.isFile() || !same2(initial, opened))
11045
11200
  throw new Error("Raw discovery input changed during open");
11046
11201
  const hash = createHash32("sha256"), buffer = Buffer.alloc(64 * 1024);
@@ -11055,13 +11210,13 @@ function hashRawDiscoveryFile(path, budget, changes) {
11055
11210
  consume(budget, count);
11056
11211
  hash.update(buffer.subarray(0, count));
11057
11212
  }
11058
- const final = fstatSync2(fd), current2 = lstatSync5(path, { throwIfNoEntry: false });
11213
+ const final = fstatSync3(fd), current2 = lstatSync5(path, { throwIfNoEntry: false });
11059
11214
  safe2(path);
11060
11215
  if (!current2?.isFile() || !same2(opened, final) || !same2(final, current2) || bytes !== final.size)
11061
11216
  throw new Error("Raw discovery input changed during read");
11062
11217
  return hash.digest("hex");
11063
11218
  } finally {
11064
- closeSync3(fd);
11219
+ closeSync4(fd);
11065
11220
  }
11066
11221
  }
11067
11222
  function identity2(stat, mutable = false) {
@@ -11107,7 +11262,7 @@ function trace(path) {
11107
11262
  cursor = dirname6(cursor);
11108
11263
  continue;
11109
11264
  }
11110
- const candidate = join9(cursor, component);
11265
+ const candidate = join92(cursor, component);
11111
11266
  if (candidate.length > AGENT_POLICY_LIMITS.pathCharacters)
11112
11267
  throw new Error("Discovery resolved path length limit exceeded");
11113
11268
  const stat = lstatSync6(candidate, { bigint: true, throwIfNoEntry: false });
@@ -12117,7 +12272,7 @@ function assertSafePath(path) {
12117
12272
  }
12118
12273
  function readRootAlias(home, agent) {
12119
12274
  assertSafePath(home);
12120
- const realHome = realpathSync3(home), alias = join16(realHome, `.${agent}`);
12275
+ const realHome = realpathSync32(home), alias = join16(realHome, `.${agent}`);
12121
12276
  const stat = lstatSync14(alias, { throwIfNoEntry: false, bigint: true });
12122
12277
  if (!stat?.isSymbolicLink())
12123
12278
  throw new Error(`Agent root alias changed after planning: ${alias}`);
@@ -12126,7 +12281,7 @@ function readRootAlias(home, agent) {
12126
12281
  const targetStat = lstatSync14(targetPath, { throwIfNoEntry: false });
12127
12282
  if (!targetStat?.isDirectory())
12128
12283
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
12129
- const target = realpathSync3(targetPath), inside = relative4(realHome, target);
12284
+ const target = realpathSync32(targetPath), inside = relative4(realHome, target);
12130
12285
  if (!inside || inside === ".." || inside.startsWith(`..${sep4}`) || isAbsolute9(inside))
12131
12286
  throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
12132
12287
  return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
@@ -12660,10 +12815,10 @@ function assertManagedAgentBridge(agent, options = {}) {
12660
12815
  if (options.profileId !== undefined && options.profileId !== profile)
12661
12816
  throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
12662
12817
  if (agent === "hermes") {
12663
- const supervisor = binding.supervisors?.hermes;
12664
- if (supervisor?.path !== join16(dataDir22, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha(renderHermesSupervisor(command, profile)))
12818
+ const supervisor2 = binding.supervisors?.hermes;
12819
+ if (supervisor2?.path !== join16(dataDir22, "agent-hooks", "hermes.js") || supervisor2?.sha256 !== sha(renderHermesSupervisor(command, profile)))
12665
12820
  throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
12666
- assertHermesProtection(home, config, command, profile, supervisor);
12821
+ assertHermesProtection(home, config, command, profile, supervisor2);
12667
12822
  } else if (agent === "opencode") {
12668
12823
  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))
12669
12824
  throw new Error("NATIVE_SKILL_DRIFT: OpenCode bridge protection changed; run skills hook install");
@@ -12955,9 +13110,9 @@ function snapshotPluginTree(root, options = {}) {
12955
13110
  const pins = directoryNames(absolute);
12956
13111
  pluginNeed(pins.length <= 1024, "Native plugin pin count exceeds its limit");
12957
13112
  directories.push({ path: absolute, stat: entryStat, names: pins });
12958
- for (const pin of pins) {
12959
- pluginNeed(/^[1-9][0-9]{0,9}$/.test(pin) && Number(pin) <= 2147483647, "Unrecognized native plugin pin name");
12960
- const pinPath = join21(absolute, pin), pinStat = stat(pinPath);
13113
+ for (const pin2 of pins) {
13114
+ pluginNeed(/^[1-9][0-9]{0,9}$/.test(pin2) && Number(pin2) <= 2147483647, "Unrecognized native plugin pin name");
13115
+ const pinPath = join21(absolute, pin2), pinStat = stat(pinPath);
12961
13116
  pluginNeed(pinStat, "Native plugin pin disappeared");
12962
13117
  const bytes2 = readBytes(pinPath, 4096, "native-pin");
12963
13118
  let value;
@@ -12967,7 +13122,7 @@ function snapshotPluginTree(root, options = {}) {
12967
13122
  pluginRefusal("Malformed native plugin process pin");
12968
13123
  }
12969
13124
  pluginKeys(value, ["pid", "procStart", "procStartFt"]);
12970
- pluginNeed(value.pid === Number(pin), "Native plugin pin identity differs from its name");
13125
+ pluginNeed(value.pid === Number(pin2), "Native plugin pin identity differs from its name");
12971
13126
  for (const key of ["procStart", "procStartFt"])
12972
13127
  pluginNeed(value[key] === undefined || typeof value[key] === "string" && value[key].length <= 128 && !/[\x00-\x1f\x7f]/.test(value[key]), "Invalid native plugin process-start witness");
12973
13128
  same3(pinStat, stat(pinPath));
@@ -13175,10 +13330,10 @@ function validatePlan(value) {
13175
13330
  pluginNeed(observedEvidenceDigest(plan.observation) === plan.evidenceDigest, "Plugin receipt observed evidence changed");
13176
13331
  pluginNeed(identityDigest(plan) === plan.planDigest, "Plugin receipt immutable identity changed");
13177
13332
  }
13178
- var quote2 = (value) => `'${value.replaceAll("'", "'\\''")}'`;
13333
+ var quote3 = (value) => `'${value.replaceAll("'", "'\\''")}'`;
13179
13334
  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"';
13180
13335
  function pluginResolverCommand(binding) {
13181
- return `${quote2("/bin/sh")} -eu -c ${quote2(resolverGuard)} plugin-resolver ${quote2(binding.target.resolver.executable)} ${quote2(binding.target.resolver.digest)} ${quote2(bindingId(binding))}`;
13336
+ return `${quote3("/bin/sh")} -eu -c ${quote3(resolverGuard)} plugin-resolver ${quote3(binding.target.resolver.executable)} ${quote3(binding.target.resolver.digest)} ${quote3(bindingId(binding))}`;
13182
13337
  }
13183
13338
  function pluginBindingPath(storeRoot, id) {
13184
13339
  pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
@@ -16905,11 +17060,11 @@ function isValidCIDRv6(value) {
16905
17060
  const parts = value.split("/");
16906
17061
  if (parts.length !== 2)
16907
17062
  return false;
16908
- const [address, prefix2] = parts;
16909
- if (!prefix2)
17063
+ const [address, prefix3] = parts;
17064
+ if (!prefix3)
16910
17065
  return false;
16911
- const prefixNum = Number(prefix2);
16912
- if (`${prefixNum}` !== prefix2)
17066
+ const prefixNum = Number(prefix3);
17067
+ if (`${prefixNum}` !== prefix3)
16913
17068
  return false;
16914
17069
  if (prefixNum < 0 || prefixNum > 128)
16915
17070
  return false;
@@ -26791,8 +26946,8 @@ function generateStringFormatCheck(doc, ctx, def, accessor) {
26791
26946
  doc.write(`if (!${accessor}.includes(${esc(def.includes)})) return INVALID;`);
26792
26947
  break;
26793
26948
  case "starts_with": {
26794
- const prefix2 = def.prefix;
26795
- doc.write(`if (${accessor}.slice(0, ${prefix2.length}) !== ${esc(prefix2)}) return INVALID;`);
26949
+ const prefix3 = def.prefix;
26950
+ doc.write(`if (${accessor}.slice(0, ${prefix3.length}) !== ${esc(prefix3)}) return INVALID;`);
26796
26951
  break;
26797
26952
  }
26798
26953
  case "ends_with": {
@@ -27525,8 +27680,8 @@ function generateDiscriminatedUnionCheck(doc, ctx, def, accessor) {
27525
27680
  claimed.add(value);
27526
27681
  }
27527
27682
  const conditions = Array.from(values, (value) => literalEquality(ctx, discVar, value));
27528
- const prefix2 = firstBranch ? "if" : "else if";
27529
- doc.write(`${prefix2} (${conditions.join(" || ")}) {`);
27683
+ const prefix3 = firstBranch ? "if" : "else if";
27684
+ doc.write(`${prefix3} (${conditions.join(" || ")}) {`);
27530
27685
  doc.indented((d) => {
27531
27686
  const branchOutput = generateCheck(d, ctx, option, accessor);
27532
27687
  d.write(`${outputVar} = ${branchOutput};`);
@@ -28380,12 +28535,12 @@ function _includes(includes, params) {
28380
28535
  includes
28381
28536
  });
28382
28537
  }
28383
- function _startsWith(prefix2, params) {
28538
+ function _startsWith(prefix3, params) {
28384
28539
  return new $ZodCheckStartsWith({
28385
28540
  check: "string_format",
28386
28541
  format: "starts_with",
28387
28542
  ...normalizeParams(params),
28388
- prefix: prefix2
28543
+ prefix: prefix3
28389
28544
  });
28390
28545
  }
28391
28546
  function _endsWith(suffix, params) {
@@ -34353,7 +34508,7 @@ function coordinateClaudeSettings(before, replacement, options) {
34353
34508
  const plan = planClaudeHookEventsUpdate({
34354
34509
  ...options,
34355
34510
  events: selected,
34356
- expectedSettingsSha256: before === null ? "absent" : createHash5("sha256").update(before).digest("hex"),
34511
+ expectedSettingsSha256: before === null ? "absent" : createHash6("sha256").update(before).digest("hex"),
34357
34512
  replacement
34358
34513
  });
34359
34514
  if (!plan)
@@ -34364,7 +34519,7 @@ function coordinateClaudeSettings(before, replacement, options) {
34364
34519
 
34365
34520
  // src/lib/installer.ts
34366
34521
  var __dirname2 = dirname7(fileURLToPath2(import.meta.url));
34367
- var HOOKS_DIR = existsSync10(join10(__dirname2, "..", "..", "hooks", "hook-gitguard")) ? join10(__dirname2, "..", "..", "hooks") : join10(__dirname2, "..", "hooks");
34522
+ var HOOKS_DIR = existsSync11(join10(__dirname2, "..", "..", "hooks", "hook-gitguard")) ? join10(__dirname2, "..", "..", "hooks") : join10(__dirname2, "..", "hooks");
34368
34523
  function normalizeHookName(name) {
34369
34524
  return name.startsWith("hook-") ? name : `hook-${name}`;
34370
34525
  }
@@ -34452,11 +34607,11 @@ function getSettingsPath(scope = "global", target = "claude", codewithConfigPath
34452
34607
  return globalOverride;
34453
34608
  const dir = getTargetSettingsDir(target);
34454
34609
  if (target === "codex")
34455
- return join10(scope === "project" ? process.cwd() : homedir5(), dir, "hooks.json");
34610
+ return join10(scope === "project" ? process.cwd() : homedir6(), dir, "hooks.json");
34456
34611
  if (scope === "project") {
34457
34612
  return target === "codewith" ? join10(process.cwd(), dir, "config.toml") : join10(process.cwd(), dir, "settings.json");
34458
34613
  }
34459
- return target === "codewith" ? join10(homedir5(), dir, "config.toml") : join10(homedir5(), dir, "settings.json");
34614
+ return target === "codewith" ? join10(homedir6(), dir, "config.toml") : join10(homedir6(), dir, "settings.json");
34460
34615
  }
34461
34616
  function getHookPath(name) {
34462
34617
  const shortName = shortHookName(name);
@@ -34466,7 +34621,7 @@ function getHookPath(name) {
34466
34621
  return join10(HOOKS_DIR, normalizeHookName(shortName));
34467
34622
  }
34468
34623
  function hookExists(name) {
34469
- return existsSync10(getHookPath(name));
34624
+ return existsSync11(getHookPath(name));
34470
34625
  }
34471
34626
  var settingsPreimages = new WeakMap;
34472
34627
  function readSettings2(scope = "global", target = "claude") {
@@ -34474,7 +34629,7 @@ function readSettings2(scope = "global", target = "claude") {
34474
34629
  if (target === "codex")
34475
34630
  return readCodexSettings(path);
34476
34631
  try {
34477
- if (existsSync10(path)) {
34632
+ if (existsSync11(path)) {
34478
34633
  const text3 = readFileSync7(path, "utf-8"), value = JSON.parse(text3);
34479
34634
  settingsPreimages.set(value, { path, text: text3 });
34480
34635
  return value;
@@ -34483,14 +34638,14 @@ function readSettings2(scope = "global", target = "claude") {
34483
34638
  console.warn("[hooks] Cannot read hook settings; repair the selected file before installation.");
34484
34639
  }
34485
34640
  const empty = {};
34486
- if (!existsSync10(path))
34641
+ if (!existsSync11(path))
34487
34642
  settingsPreimages.set(empty, { path, text: null });
34488
34643
  return empty;
34489
34644
  }
34490
34645
  function settingsIdentity(path) {
34491
- if (existsSync10(path))
34646
+ if (existsSync11(path))
34492
34647
  return realpathSync5(path);
34493
- return existsSync10(dirname7(path)) ? join10(realpathSync5(dirname7(path)), basename3(path)) : resolve7(path);
34648
+ return existsSync11(dirname7(path)) ? join10(realpathSync5(dirname7(path)), basename3(path)) : resolve7(path);
34494
34649
  }
34495
34650
  function writeSettings(settings, scope = "global", target = "claude", expectedSettingsSha256) {
34496
34651
  const path = getSettingsPath(scope, target);
@@ -34502,21 +34657,21 @@ function writeSettings(settings, scope = "global", target = "claude", expectedSe
34502
34657
  throw new Error("Hook settings require a valid captured preimage.");
34503
34658
  assertSettingsPreimage(captured.text, expectedSettingsSha256);
34504
34659
  }
34505
- if (target === "claude" && settingsIdentity(path) === settingsIdentity(join10(homedir5(), ".claude/settings.json"))) {
34660
+ if (target === "claude" && settingsIdentity(path) === settingsIdentity(join10(homedir6(), ".claude/settings.json"))) {
34506
34661
  const before = settingsPreimages.get(settings);
34507
34662
  if (!before || before.path !== path)
34508
34663
  throw new Error("Claude settings require a valid captured preimage before installation.");
34509
- const current2 = existsSync10(path) ? readFileSync7(path, "utf8") : null;
34664
+ const current2 = existsSync11(path) ? readFileSync7(path, "utf8") : null;
34510
34665
  if (current2 !== before.text)
34511
34666
  throw new Error("Claude settings changed during installation; no settings were written.");
34512
34667
  if (coordinateClaudeSettings(before.text, JSON.stringify(settings, null, 2) + `
34513
- `, { home: homedir5() }))
34668
+ `, { home: homedir6() }))
34514
34669
  return;
34515
34670
  }
34516
34671
  if (expectedSettingsSha256 !== undefined)
34517
34672
  return writeHookSettingsPreimage(path, settings, captured.text);
34518
34673
  const dir = dirname7(path);
34519
- if (!existsSync10(dir)) {
34674
+ if (!existsSync11(dir)) {
34520
34675
  mkdirSync6(dir, { recursive: true });
34521
34676
  }
34522
34677
  writeFileSync5(path, JSON.stringify(settings, null, 2) + `
@@ -34615,7 +34770,7 @@ function buildCodewithTomlFragment(name, profile, mementos) {
34615
34770
  function readCodewithConfig(scope, configPath) {
34616
34771
  const path = getSettingsPath(scope, "codewith", configPath);
34617
34772
  try {
34618
- return existsSync10(path) ? readFileSync7(path, "utf-8") : "";
34773
+ return existsSync11(path) ? readFileSync7(path, "utf-8") : "";
34619
34774
  } catch {
34620
34775
  return "";
34621
34776
  }
@@ -34623,9 +34778,9 @@ function readCodewithConfig(scope, configPath) {
34623
34778
  function appendCodewithFragment(fragment, scope, configPath) {
34624
34779
  const path = getSettingsPath(scope, "codewith", configPath);
34625
34780
  const dir = dirname7(path);
34626
- if (!existsSync10(dir))
34781
+ if (!existsSync11(dir))
34627
34782
  mkdirSync6(dir, { recursive: true });
34628
- const existing = existsSync10(path) ? readFileSync7(path, "utf-8") : "";
34783
+ const existing = existsSync11(path) ? readFileSync7(path, "utf-8") : "";
34629
34784
  const sep3 = existing.trim() ? `
34630
34785
 
34631
34786
  ` : "";
@@ -34805,7 +34960,20 @@ function installHook(name, options = {}) {
34805
34960
  return installForTarget(name, scope, overwrite, target, profile, codewithMode, codewithConfigPath, mementos, expectedSettingsSha256);
34806
34961
  }
34807
34962
  function registerHook(name, scope = "global", target = "claude", profile, mementos, write = writeSettings) {
34808
- const meta3 = isNativeSafetyName(name) ? getHook(name) : resolveHookMeta(name);
34963
+ const readiness = name === "trash-readiness";
34964
+ if (readiness && (target === "gemini" || profile || scope !== "global"))
34965
+ throw new Error("Unsupported readiness target");
34966
+ const meta3 = readiness ? {
34967
+ name,
34968
+ event: "SessionStart",
34969
+ matcher: "^(startup|resume|clear|fork)$",
34970
+ timeoutSeconds: 20,
34971
+ displayName: "Trash Readiness",
34972
+ description: "Read-only native startup readiness",
34973
+ version: "0.1.0",
34974
+ category: "Environment",
34975
+ tags: ["trash", "startup"]
34976
+ } : isNativeSafetyName(name) ? getHook(name) : resolveHookMeta(name);
34809
34977
  if (!meta3)
34810
34978
  return;
34811
34979
  const eventKeys = getHookEvents(meta3).map((event) => {
@@ -34822,18 +34990,20 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
34822
34990
  const settings = readSettings2(scope, target);
34823
34991
  if (!settings.hooks)
34824
34992
  settings.hooks = {};
34825
- if (isNativeSafetyName(name)) {
34993
+ if (isNativeSafetyName(name) || readiness) {
34826
34994
  for (const groups of Object.values(settings.hooks))
34827
34995
  for (const group of groups)
34828
34996
  for (const hook of group.hooks ?? []) {
34829
34997
  if (typeof hook.command === "string" && /(?:^|\s)hasna-native-safety-v\d+(?:\s|$)/.test(hook.command) && !registrationName(hook.command)) {
34830
34998
  throw new Error("Unknown managed safety definition; reconcile its exact version before replacing it.");
34831
34999
  }
35000
+ if (readiness && typeof hook.command === "string" && hook.command.includes("hasna-native-readiness-v") && registrationName(hook.command) !== "trash-readiness")
35001
+ throw new Error("Unknown managed readiness definition");
34832
35002
  }
34833
35003
  }
34834
35004
  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;
34835
- const hookCommand = isNativeSafetyName(name) ? installedNativeSafetyCommand(name) : (profile ? `hooks run ${name} --profile ${profile}` : `hooks run ${name}`) + mementosCommandSuffix(mementos ?? retained);
34836
- if (target === "codex" || target === "claude" && isNativeSafetyName(name)) {
35005
+ const hookCommand = readiness ? installedNativeReadinessCommand(target) : isNativeSafetyName(name) ? installedNativeSafetyCommand(name) : (profile ? `hooks run ${name} --profile ${profile}` : `hooks run ${name}`) + mementosCommandSuffix(mementos ?? retained);
35006
+ if (target === "codex" || target === "claude" && (isNativeSafetyName(name) || readiness)) {
34837
35007
  const positions = [];
34838
35008
  for (const [event, groups] of Object.entries(settings.hooks)) {
34839
35009
  for (const entry of groups) {
@@ -34843,8 +35013,8 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
34843
35013
  if (match && (match[1] ?? match[2]) === name || registrationName(command) === name) {
34844
35014
  if (registrationName(command) !== name)
34845
35015
  throw new Error("Unrecognized Codex registration; reconcile it before updating this hook.");
34846
- if (isNativeSafetyName(name) && entry.hooks[index].async !== undefined && entry.hooks[index].async !== false)
34847
- throw new Error("Asynchronous safety hooks cannot block; reconcile this registration before updating it.");
35016
+ if ((isNativeSafetyName(name) || readiness) && (entry.hooks[index].async !== undefined && entry.hooks[index].async !== false || entry.hooks[index].asyncRewake === true))
35017
+ throw new Error("Asynchronous managed native hooks are refused; synchronous registration is required");
34848
35018
  positions.push({ event, entry, index });
34849
35019
  }
34850
35020
  }
@@ -34964,12 +35134,12 @@ function removeHookForTarget(name, scope, target) {
34964
35134
 
34965
35135
  // src/lib/profiles.ts
34966
35136
  init_app_home();
34967
- import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync8, readdirSync as readdirSync8, rmSync, cpSync as cpSync2 } from "fs";
34968
- import { join as join12 } from "path";
35137
+ import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync8, readdirSync as readdirSync8, rmSync, cpSync as cpSync2 } from "fs";
35138
+ import { join as join11 } from "path";
34969
35139
  function getProfilesDir() {
34970
- const newDir = join12(getEffectiveDataRoot(), "profiles");
34971
- const oldDir = join12(getHomeDir(), ".hooks", "profiles");
34972
- if (!existsSync11(newDir) && existsSync11(oldDir)) {
35140
+ const newDir = join11(getEffectiveDataRoot(), "profiles");
35141
+ const oldDir = join11(getHomeDir(), ".hooks", "profiles");
35142
+ if (!existsSync12(newDir) && existsSync12(oldDir)) {
34973
35143
  mkdirSync9(newDir, { recursive: true });
34974
35144
  cpSync2(oldDir, newDir, { recursive: true });
34975
35145
  }
@@ -34977,12 +35147,12 @@ function getProfilesDir() {
34977
35147
  }
34978
35148
  function ensureProfilesDir() {
34979
35149
  const dir = getProfilesDir();
34980
- if (!existsSync11(dir)) {
35150
+ if (!existsSync12(dir)) {
34981
35151
  mkdirSync9(dir, { recursive: true });
34982
35152
  }
34983
35153
  }
34984
35154
  function profilePath(id) {
34985
- return join12(getProfilesDir(), `${id}.json`);
35155
+ return join11(getProfilesDir(), `${id}.json`);
34986
35156
  }
34987
35157
  function shortUuid() {
34988
35158
  return crypto.randomUUID().slice(0, 8);
@@ -35008,23 +35178,23 @@ function createProfile(input2) {
35008
35178
  function getProfile(id) {
35009
35179
  const path = profilePath(id);
35010
35180
  try {
35011
- if (!existsSync11(path))
35181
+ if (!existsSync12(path))
35012
35182
  return null;
35013
- return JSON.parse(readFileSync9(path, "utf-8"));
35183
+ return JSON.parse(readFileSync8(path, "utf-8"));
35014
35184
  } catch {
35015
35185
  return null;
35016
35186
  }
35017
35187
  }
35018
35188
  function listProfiles() {
35019
35189
  const dir = getProfilesDir();
35020
- if (!existsSync11(dir))
35190
+ if (!existsSync12(dir))
35021
35191
  return [];
35022
35192
  try {
35023
35193
  const files = readdirSync8(dir).filter((f) => f.endsWith(".json"));
35024
35194
  const profiles = [];
35025
35195
  for (const file2 of files) {
35026
35196
  try {
35027
- const content = readFileSync9(join12(dir, file2), "utf-8");
35197
+ const content = readFileSync8(join11(dir, file2), "utf-8");
35028
35198
  profiles.push(JSON.parse(content));
35029
35199
  } catch {}
35030
35200
  }
@@ -35563,9 +35733,9 @@ var __dirname3 = dirname19(fileURLToPath3(import.meta.url));
35563
35733
  var pkg = { name: "@hasna/hooks", version: "0.0.0" };
35564
35734
  try {
35565
35735
  for (const rel of ["../../package.json", "../package.json", "../../../package.json"]) {
35566
- const p = join17(__dirname3, rel);
35567
- if (existsSync13(p)) {
35568
- pkg = JSON.parse(readFileSync12(p, "utf-8"));
35736
+ const p = join13(__dirname3, rel);
35737
+ if (existsSync14(p)) {
35738
+ pkg = JSON.parse(readFileSync9(p, "utf-8"));
35569
35739
  break;
35570
35740
  }
35571
35741
  }
@@ -35574,10 +35744,10 @@ async function runVerifiedHook(name, stdin, timeoutMs) {
35574
35744
  const { resolveHook: resolveHook2, resolveScriptPath: resolveScriptPath2 } = await Promise.resolve().then(() => (init_resolve(), exports_resolve));
35575
35745
  const resolved = resolveHook2(name);
35576
35746
  const hookScript = resolveScriptPath2(name);
35577
- if (!resolved || !hookScript || !existsSync13(hookScript)) {
35747
+ if (!resolved || !hookScript || !existsSync14(hookScript)) {
35578
35748
  throw new Error(`Hook '${name}' not found`);
35579
35749
  }
35580
- const content = readFileSync12(hookScript);
35750
+ const content = readFileSync9(hookScript);
35581
35751
  const sha4 = sha256Of(content);
35582
35752
  const check2 = checkScriptHash(name, sha4);
35583
35753
  if (!check2.ok) {
@@ -35783,7 +35953,7 @@ function createHooksServer() {
35783
35953
  const settingsPath = getSettingsPath(scope);
35784
35954
  const issues = [];
35785
35955
  const healthy = [];
35786
- const settingsExist = existsSync13(settingsPath);
35956
+ const settingsExist = existsSync14(settingsPath);
35787
35957
  if (!settingsExist) {
35788
35958
  issues.push({ hook: "(settings)", issue: `${settingsPath} not found`, severity: "warning" });
35789
35959
  }
@@ -35793,7 +35963,7 @@ function createHooksServer() {
35793
35963
  const meta3 = custom2 ? resolveHookMeta(name) : getHook(name);
35794
35964
  let hookHealthy = true;
35795
35965
  if (custom2) {
35796
- if (!existsSync13(custom2.scriptPath)) {
35966
+ if (!existsSync14(custom2.scriptPath)) {
35797
35967
  issues.push({ hook: name, issue: `Custom hook script missing: ${custom2.scriptPath}`, severity: "error" });
35798
35968
  hookHealthy = false;
35799
35969
  }
@@ -35803,14 +35973,14 @@ function createHooksServer() {
35803
35973
  continue;
35804
35974
  }
35805
35975
  const hookDir = getHookPath(name);
35806
- if (!existsSync13(join17(hookDir, "src", "hook.ts"))) {
35976
+ if (!existsSync14(join13(hookDir, "src", "hook.ts"))) {
35807
35977
  issues.push({ hook: name, issue: "Missing src/hook.ts in package", severity: "error" });
35808
35978
  hookHealthy = false;
35809
35979
  }
35810
35980
  }
35811
35981
  if (meta3 && settingsExist) {
35812
35982
  try {
35813
- const settings = JSON.parse(readFileSync12(settingsPath, "utf-8"));
35983
+ const settings = JSON.parse(readFileSync9(settingsPath, "utf-8"));
35814
35984
  if (!hookRegisteredInSettings(settings, name, meta3.event, meta3.matcher)) {
35815
35985
  const eventName = meta3.event.split(":")[0];
35816
35986
  issues.push({ hook: name, issue: `Not registered under correct event (${eventName})`, severity: "error" });
@@ -35847,10 +36017,10 @@ function createHooksServer() {
35847
36017
  return { content: [{ type: "text", text: JSON.stringify({ error: `Hook '${name}' not found` }) }] };
35848
36018
  }
35849
36019
  const hookPath = getHookPath(name);
35850
- const readmePath = join17(hookPath, "README.md");
36020
+ const readmePath = join13(hookPath, "README.md");
35851
36021
  let readme = "";
35852
- if (existsSync13(readmePath)) {
35853
- readme = readFileSync12(readmePath, "utf-8");
36022
+ if (existsSync14(readmePath)) {
36023
+ readme = readFileSync9(readmePath, "utf-8");
35854
36024
  }
35855
36025
  if (verbose) {
35856
36026
  return { content: [{ type: "text", text: JSON.stringify({ ...meta3, readme }) }] };
@@ -36033,7 +36203,7 @@ function createHooksServer() {
36033
36203
  const ctx = {
36034
36204
  scope,
36035
36205
  settings_path: settingsPath,
36036
- settings_exists: existsSync13(settingsPath),
36206
+ settings_exists: existsSync14(settingsPath),
36037
36207
  registered_hooks: hooks,
36038
36208
  hook_count: hooks.length,
36039
36209
  healthy,
@@ -36179,8 +36349,8 @@ function createHooksServer() {
36179
36349
  const settingsPath = getSettingsPath(scope);
36180
36350
  let settings = {};
36181
36351
  try {
36182
- if (existsSync13(settingsPath))
36183
- settings = JSON.parse(readFileSync12(settingsPath, "utf-8"));
36352
+ if (existsSync14(settingsPath))
36353
+ settings = JSON.parse(readFileSync9(settingsPath, "utf-8"));
36184
36354
  } catch {}
36185
36355
  if (!settings.hooks)
36186
36356
  settings.hooks = {};
@@ -36202,8 +36372,8 @@ function createHooksServer() {
36202
36372
  const settingsPath = getSettingsPath(scope);
36203
36373
  let settings = {};
36204
36374
  try {
36205
- if (existsSync13(settingsPath))
36206
- settings = JSON.parse(readFileSync12(settingsPath, "utf-8"));
36375
+ if (existsSync14(settingsPath))
36376
+ settings = JSON.parse(readFileSync9(settingsPath, "utf-8"));
36207
36377
  } catch {}
36208
36378
  if (settings.hooks?.__disabled) {
36209
36379
  settings.hooks.__disabled = settings.hooks.__disabled.filter((n) => n !== name);