@hasna/skills 0.10.72 → 0.10.74

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
@@ -6988,8 +6988,8 @@ var require_public_api = __commonJS((exports) => {
6988
6988
  });
6989
6989
 
6990
6990
  // src/lib/registry.ts
6991
- import { existsSync as existsSync15, readFileSync as readFileSync26, readdirSync as readdirSync14 } from "fs";
6992
- import { join as join40 } from "path";
6991
+ import { existsSync as existsSync15, readFileSync as readFileSync27, readdirSync as readdirSync14 } from "fs";
6992
+ import { join as join41 } from "path";
6993
6993
 
6994
6994
  // src/lib/config.ts
6995
6995
  import { closeSync, constants, existsSync as existsSync2, fstatSync, lstatSync, openSync, readFileSync, statSync, writeFileSync, mkdirSync } from "fs";
@@ -7266,7 +7266,7 @@ import {
7266
7266
  statSync as statSync13,
7267
7267
  writeFileSync as writeFileSync9
7268
7268
  } from "fs";
7269
- import { basename as basename17, dirname as dirname27, isAbsolute as isAbsolute29, join as join39, normalize as normalize4, relative as relative11, resolve as resolve28, sep as sep16 } from "path";
7269
+ import { basename as basename17, dirname as dirname28, isAbsolute as isAbsolute29, join as join40, normalize as normalize4, relative as relative11, resolve as resolve29, sep as sep17 } from "path";
7270
7270
 
7271
7271
  // src/lib/registry-data/index.ts
7272
7272
  var SKILLS = [];
@@ -8566,8 +8566,8 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
8566
8566
  displayName: manifest.displayName ?? displayName(manifest.name),
8567
8567
  category: manifest.category ?? "Development Tools",
8568
8568
  tags: manifest.tags?.length ? manifest.tags : ["custom", manifest.name],
8569
- inputs: [],
8570
- commands: []
8569
+ inputs: Array.isArray(manifest.inputs) ? manifest.inputs : [],
8570
+ commands: Array.isArray(manifest.commands) ? manifest.commands : []
8571
8571
  };
8572
8572
  if (!existsSync6(join6(skillPath, "SKILL.md"))) {
8573
8573
  writeFileSync2(join6(skillPath, "SKILL.md"), renderSkillMd(next));
@@ -8924,9 +8924,9 @@ var NATIVE_SKILL_ROOTS = [
8924
8924
  ];
8925
8925
 
8926
8926
  // src/lib/authoring-path.ts
8927
- import { lstatSync as lstatSync32, realpathSync as realpathSync14 } from "fs";
8927
+ import { lstatSync as lstatSync33, realpathSync as realpathSync14 } from "fs";
8928
8928
  import { homedir as homedir6 } from "os";
8929
- import { basename as basename16, dirname as dirname26, join as join38, resolve as resolve27, sep as sep15 } from "path";
8929
+ import { basename as basename16, dirname as dirname27, join as join39, resolve as resolve28, sep as sep16 } from "path";
8930
8930
 
8931
8931
  // src/lib/agent-adapters.ts
8932
8932
  var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes", "sumi"];
@@ -9973,8 +9973,8 @@ function assertAgentPolicyCollections(policy) {
9973
9973
 
9974
9974
  // src/lib/agent-discovery.ts
9975
9975
  import { createHash as createHash30 } from "crypto";
9976
- import { lstatSync as lstatSync31, readFileSync as readFileSync25, readdirSync as readdirSync12, realpathSync as realpathSync13 } from "fs";
9977
- import { basename as basename15, dirname as dirname25, isAbsolute as isAbsolute28, join as join37, relative as relative10, resolve as resolve26, sep as sep14 } from "path";
9976
+ import { lstatSync as lstatSync32, readFileSync as readFileSync26, readdirSync as readdirSync12, realpathSync as realpathSync13 } from "fs";
9977
+ import { basename as basename15, dirname as dirname26, isAbsolute as isAbsolute28, join as join38, relative as relative10, resolve as resolve27, sep as sep15 } from "path";
9978
9978
  import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
9979
9979
 
9980
9980
  // node_modules/yaml/dist/index.js
@@ -12430,6 +12430,10 @@ function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
12430
12430
  function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
12431
12431
  need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
12432
12432
  budget2.remaining -= Buffer.byteLength(text2);
12433
+ const prepared = prepareCodexSettingsV1(text2);
12434
+ return finishCodexSettingsV1(prepared.config, prepared.tokens);
12435
+ }
12436
+ function prepareCodexSettingsV1(text2) {
12433
12437
  let config;
12434
12438
  try {
12435
12439
  config = Bun.TOML.parse(text2);
@@ -12437,6 +12441,9 @@ function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 *
12437
12441
  throw new Error("Invalid Codex settings witness");
12438
12442
  }
12439
12443
  need4(config && typeof config === "object" && !Array.isArray(config));
12444
+ return { config, tokens: numericTokens(text2) };
12445
+ }
12446
+ function finishCodexSettingsV1(config, tokens) {
12440
12447
  if (Object.hasOwn(config, "model")) {
12441
12448
  need4(typeof config.model === "string" && /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/.test(config.model));
12442
12449
  delete config.model;
@@ -12446,7 +12453,7 @@ function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 *
12446
12453
  need4(typeof config[key] === "string" && values.has(config[key]));
12447
12454
  delete config[key];
12448
12455
  }
12449
- return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical4(config), numericTokens(text2)])}`);
12456
+ return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical4(config), tokens])}`);
12450
12457
  }
12451
12458
  function captureCodexSettings(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
12452
12459
  const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
@@ -13160,7 +13167,7 @@ function projectStatusLine(config) {
13160
13167
  if (!Object.keys(tui).length)
13161
13168
  delete config.tui;
13162
13169
  }
13163
- function settingsDigest2(text2, version = 2, dependencies) {
13170
+ function prepareSettings(text2, version, dependencies) {
13164
13171
  let config;
13165
13172
  try {
13166
13173
  config = Bun.TOML.parse(text2);
@@ -13199,18 +13206,87 @@ function settingsDigest2(text2, version = 2, dependencies) {
13199
13206
  delete config.tui.model_availability_nux;
13200
13207
  if (isRecord3(config.tui) && typeof config.tui.screen_reader_detection_done === "boolean")
13201
13208
  delete config.tui.screen_reader_detection_done;
13202
- config = embedCodexNumericTokens(config, tokens);
13209
+ return { config: embedCodexNumericTokens(config, tokens), numericSource: text2, dependencies };
13210
+ }
13211
+ const mcp = version >= 3 ? projectRegularCodexMcp(config, text2) : { config, numericSource: text2 };
13212
+ return version >= 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
13213
+ }
13214
+ function finishSettings(prepared, version) {
13215
+ const config = prepared.config;
13216
+ if (version === 6) {
13203
13217
  normalize2(config, 6);
13204
13218
  if (isRecord3(config.tui) && !Object.keys(config.tui).length)
13205
13219
  delete config.tui;
13206
- return sha2(`hasna.skills.codex-settings.v6\x00${JSON.stringify([canonical5(config), canonical5(dependencies)])}`);
13220
+ return sha2(`hasna.skills.codex-settings.v6\x00${JSON.stringify([canonical5(config), canonical5(prepared.dependencies)])}`);
13207
13221
  }
13208
- const mcp = version >= 3 ? projectRegularCodexMcp(config, text2) : { config, numericSource: text2 };
13209
- const projected = version >= 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
13210
- normalize2(projected.config, version);
13211
- const values = [canonical5(projected.config), numericTokens2(projected.numericSource)];
13222
+ normalize2(config, version);
13223
+ const values = [canonical5(config), numericTokens2(prepared.numericSource)];
13212
13224
  return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify(values)}`);
13213
13225
  }
13226
+ function settingsDigest2(text2, version = 2, dependencies) {
13227
+ return finishSettings(prepareSettings(text2, version, dependencies), version);
13228
+ }
13229
+ var SCREEN_READER_RECEIPT = "screen_reader_detection_done";
13230
+ function cloneTree(value) {
13231
+ if (Array.isArray(value))
13232
+ return value.map(cloneTree);
13233
+ if (isRecord3(value) && [Object.prototype, null].includes(Object.getPrototypeOf(value)))
13234
+ return Object.fromEntries(Object.entries(value).map(([key2, child2]) => [key2, cloneTree(child2)]));
13235
+ return value;
13236
+ }
13237
+ function receiptEdits(config) {
13238
+ const tui = config.tui;
13239
+ const set = (value) => (target) => {
13240
+ if (!isRecord3(target.tui))
13241
+ target.tui = {};
13242
+ target.tui[SCREEN_READER_RECEIPT] = value;
13243
+ };
13244
+ if (tui === undefined || isRecord3(tui) && !Object.hasOwn(tui, SCREEN_READER_RECEIPT))
13245
+ return [set(true), set(false)];
13246
+ if (!isRecord3(tui) || typeof tui[SCREEN_READER_RECEIPT] !== "boolean")
13247
+ return [];
13248
+ const remove = (dropEmpty) => (target) => {
13249
+ delete target.tui[SCREEN_READER_RECEIPT];
13250
+ if (dropEmpty && !Object.keys(target.tui).length)
13251
+ delete target.tui;
13252
+ };
13253
+ return [remove(true), ...Object.keys(tui).length === 1 ? [remove(false)] : [], set(!tui[SCREEN_READER_RECEIPT])];
13254
+ }
13255
+ function codexSettingsToleratesScreenReaderReceipt(text2, hashMode, expected) {
13256
+ try {
13257
+ if (Buffer.byteLength(text2) > 1024 * 1024 || !/^[a-f0-9]{64}$/.test(expected))
13258
+ return false;
13259
+ if (hashMode === "codex-settings-v1") {
13260
+ const prepared2 = prepareCodexSettingsV1(text2);
13261
+ return receiptEdits(prepared2.config).some((edit) => {
13262
+ const candidate = cloneTree(prepared2.config);
13263
+ edit(candidate);
13264
+ return finishCodexSettingsV1(candidate, prepared2.tokens) === expected;
13265
+ });
13266
+ }
13267
+ const version = Number(hashMode.slice("codex-settings-v".length));
13268
+ if (![2, 3, 4, 5].includes(version))
13269
+ return false;
13270
+ const prepared = prepareSettings(text2, version);
13271
+ return receiptEdits(prepared.config).some((edit) => {
13272
+ const candidate = cloneTree(prepared.config);
13273
+ edit(candidate);
13274
+ if (version >= 4 && isRecord3(candidate.tui) && !Object.keys(candidate.tui).length)
13275
+ delete candidate.tui;
13276
+ return finishSettings({ ...prepared, config: candidate }, version) === expected;
13277
+ });
13278
+ } catch {
13279
+ return false;
13280
+ }
13281
+ }
13282
+ function codexSettingsWitnessMatch(path, hashMode, expected, budget2 = { remaining: 256 * 1024 * 1024 }) {
13283
+ const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
13284
+ need7(Buffer.byteLength(text2) <= 1024 * 1024);
13285
+ const exact = hashMode === "codex-settings-v1" ? hashCodexSettingsReplacement(text2) : settingsDigest2(text2, Number(hashMode.slice("codex-settings-v".length)));
13286
+ if (exact === expected)
13287
+ return "exact";
13288
+ return codexSettingsToleratesScreenReaderReceipt(text2, hashMode, expected) ? "screen-reader-receipt" : null;
13289
+ }
13214
13290
  function hashCodexSettingsReplacementV2(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
13215
13291
  need7(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
13216
13292
  budget2.remaining -= Buffer.byteLength(text2);
@@ -13925,6 +14001,8 @@ import { execFile, spawn as spawn2 } from "child_process";
13925
14001
  import { realpathSync as realpathSync4, statSync as statSync8 } from "fs";
13926
14002
  import { isAbsolute as isAbsolute14 } from "path";
13927
14003
  import { promisify } from "util";
14004
+ var CODEX_CONFIG_FALLBACK_SUMMARY = "Invalid configuration; using defaults.";
14005
+ var CODEX_CONFIG_INVALID = "CODEX_HOOK_TRUST_NATIVE_CONFIG_INVALID: Codex could not load its configuration and fell back to defaults; repair config.toml (run codex once to see the error) and retry";
13928
14006
  async function connectCodexHookRpc(options) {
13929
14007
  const timeoutMs = options.timeoutMs ?? 20000;
13930
14008
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 20000)
@@ -13953,7 +14031,7 @@ async function connectCodexHookRpc(options) {
13953
14031
  else
13954
14032
  env.CODEX_HOME = options.codexHome;
13955
14033
  const corpusLease = acquireCodexCorpusWrite([options.codexHome ?? join17(options.home, ".codex")], { codexCommand: binary });
13956
- const child2 = spawn2(binary, ["app-server", "--strict-config", "--stdio"], {
14034
+ const child2 = spawn2(binary, ["app-server", "--stdio"], {
13957
14035
  cwd: options.home,
13958
14036
  env,
13959
14037
  stdio: ["pipe", "pipe", "pipe"]
@@ -13965,12 +14043,12 @@ async function connectCodexHookRpc(options) {
13965
14043
  resolve15();
13966
14044
  }));
13967
14045
  const pending = new Map;
13968
- let nextId = 1, buffer = Buffer.alloc(0), stopped = false, diagnosticBytes = 0;
14046
+ let nextId = 1, buffer = Buffer.alloc(0), stopped = false, diagnosticBytes = 0, configInvalid = false;
13969
14047
  const fail2 = () => {
13970
14048
  stopped = true;
13971
14049
  for (const value of pending.values()) {
13972
14050
  clearTimeout(value.timer);
13973
- value.reject(new Error("CODEX_HOOK_TRUST_NATIVE_RPC_FAILED"));
14051
+ value.reject(new Error(configInvalid ? CODEX_CONFIG_INVALID : "CODEX_HOOK_TRUST_NATIVE_RPC_FAILED"));
13974
14052
  }
13975
14053
  pending.clear();
13976
14054
  };
@@ -14010,6 +14088,12 @@ async function connectCodexHookRpc(options) {
14010
14088
  child2.kill();
14011
14089
  return;
14012
14090
  }
14091
+ if (!("id" in value) && value.method === "configWarning" && value.params?.summary === CODEX_CONFIG_FALLBACK_SUMMARY) {
14092
+ configInvalid = true;
14093
+ fail2();
14094
+ child2.kill();
14095
+ return;
14096
+ }
14013
14097
  const waiting = pending.get(value.id);
14014
14098
  if (!waiting)
14015
14099
  continue;
@@ -14026,7 +14110,7 @@ async function connectCodexHookRpc(options) {
14026
14110
  processId: child2.pid,
14027
14111
  request(method, params) {
14028
14112
  if (stopped)
14029
- return Promise.reject(new Error("CODEX_HOOK_TRUST_NATIVE_RPC_CLOSED"));
14113
+ return Promise.reject(new Error(configInvalid ? CODEX_CONFIG_INVALID : "CODEX_HOOK_TRUST_NATIVE_RPC_CLOSED"));
14030
14114
  return new Promise((resolve15, reject) => {
14031
14115
  const id = nextId++;
14032
14116
  const timer = setTimeout(() => {
@@ -14069,7 +14153,7 @@ async function connectCodexHookRpc(options) {
14069
14153
  return rpc;
14070
14154
  } catch {
14071
14155
  await rpc.close();
14072
- throw new Error("CODEX_HOOK_TRUST_NATIVE_UNSUPPORTED: native hooks/list and versioned config writes are required");
14156
+ throw new Error(configInvalid ? CODEX_CONFIG_INVALID : "CODEX_HOOK_TRUST_NATIVE_UNSUPPORTED: native hooks/list and versioned config writes are required");
14073
14157
  }
14074
14158
  }
14075
14159
 
@@ -15053,14 +15137,14 @@ function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
15053
15137
  }
15054
15138
 
15055
15139
  // src/lib/plugin-discovery.ts
15056
- import { closeSync as closeSync18, constants as constants19, fstatSync as fstatSync18, lstatSync as lstatSync29, openSync as openSync18, opendirSync as opendirSync5, readSync as readSync13 } from "fs";
15057
- import { basename as basename13, dirname as dirname23, isAbsolute as isAbsolute26, join as join36, resolve as resolve24 } from "path";
15140
+ import { closeSync as closeSync18, constants as constants19, fstatSync as fstatSync18, lstatSync as lstatSync30, openSync as openSync18, opendirSync as opendirSync5, readSync as readSync13 } from "fs";
15141
+ import { basename as basename13, dirname as dirname24, isAbsolute as isAbsolute26, join as join37, resolve as resolve25 } from "path";
15058
15142
 
15059
15143
  // src/lib/plugin-admission.ts
15060
15144
  import { homedir as homedir5 } from "os";
15061
- import { isAbsolute as isAbsolute25, join as join35, resolve as resolve23 } from "path";
15145
+ import { isAbsolute as isAbsolute25, join as join36, resolve as resolve24 } from "path";
15062
15146
  // package.json
15063
- var version = "0.10.72";
15147
+ var version = "0.10.74";
15064
15148
  var package_default = {
15065
15149
  name: "@hasna/skills",
15066
15150
  version,
@@ -16905,22 +16989,22 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
16905
16989
  }
16906
16990
 
16907
16991
  // src/lib/skillinfo.ts
16908
- import { existsSync as existsSync13, readFileSync as readFileSync23 } from "fs";
16909
- import { join as join34 } from "path";
16992
+ import { existsSync as existsSync13, readFileSync as readFileSync25 } from "fs";
16993
+ import { join as join35 } from "path";
16910
16994
 
16911
16995
  // src/lib/installer.ts
16912
- import { existsSync as existsSync12, readFileSync as readFileSync21, rmSync as rmSync3 } from "fs";
16913
- import { join as join33 } from "path";
16996
+ import { existsSync as existsSync12, readFileSync as readFileSync23, rmSync as rmSync3 } from "fs";
16997
+ import { join as join34 } from "path";
16914
16998
  import { homedir as homedir4 } from "os";
16915
16999
 
16916
17000
  // src/lib/agent-sync.ts
16917
17001
  import {
16918
17002
  cpSync as cpSync2,
16919
17003
  existsSync as existsSync10,
16920
- lstatSync as lstatSync28,
17004
+ lstatSync as lstatSync29,
16921
17005
  mkdirSync as mkdirSync9,
16922
17006
  mkdtempSync,
16923
- readFileSync as readFileSync19,
17007
+ readFileSync as readFileSync20,
16924
17008
  readdirSync as readdirSync11,
16925
17009
  renameSync as renameSync5,
16926
17010
  rmSync as rmSync2,
@@ -16928,7 +17012,7 @@ import {
16928
17012
  writeFileSync as writeFileSync7
16929
17013
  } from "fs";
16930
17014
  import { homedir as homedir3 } from "os";
16931
- import { basename as basename12, dirname as dirname22, join as join31 } from "path";
17015
+ import { basename as basename12, dirname as dirname23, join as join32 } from "path";
16932
17016
 
16933
17017
  // src/lib/codex-native-skill-policy.ts
16934
17018
  import { createHash as createHash21, randomUUID as randomUUID3 } from "crypto";
@@ -18270,16 +18354,59 @@ function assertCodexSemanticCacheWitnessUnchanged(previous, current) {
18270
18354
  refuse4();
18271
18355
  }
18272
18356
 
18357
+ // src/lib/codex-node-modules.ts
18358
+ import { lstatSync as lstatSync21, readFileSync as readFileSync16 } from "fs";
18359
+ import { dirname as dirname19, join as join26, resolve as resolve19, sep as sep12 } from "path";
18360
+ var PLUGIN_MANIFESTS = Object.freeze(["plugin.json", ".codex-plugin/plugin.json", ".claude-plugin/plugin.json", ".cursor-plugin/plugin.json"]);
18361
+ var PLUGIN_ROOT_DEPTH = 3;
18362
+ function codexPluginCacheSkillTree(path, depth) {
18363
+ if (depth <= PLUGIN_ROOT_DEPTH)
18364
+ return true;
18365
+ let root = path;
18366
+ for (let level = depth;level > PLUGIN_ROOT_DEPTH; level--)
18367
+ root = dirname19(root);
18368
+ try {
18369
+ const skillRoots = [join26(root, "skills")];
18370
+ for (const name of PLUGIN_MANIFESTS) {
18371
+ const manifest = join26(root, name), stat2 = lstatSync21(manifest, { throwIfNoEntry: false });
18372
+ if (!stat2)
18373
+ continue;
18374
+ if (!stat2.isFile() || stat2.size > 1024 * 1024)
18375
+ return true;
18376
+ const text2 = new TextDecoder("utf-8", { fatal: true }).decode(readFileSync16(manifest));
18377
+ hashNativeJsonControls(text2);
18378
+ const value = JSON.parse(text2);
18379
+ if (!value || typeof value !== "object" || Array.isArray(value))
18380
+ return true;
18381
+ const declared = value.skills;
18382
+ if (declared === undefined)
18383
+ continue;
18384
+ const paths = typeof declared === "string" ? [declared] : declared;
18385
+ if (!Array.isArray(paths) || paths.some((item) => typeof item !== "string"))
18386
+ return true;
18387
+ for (const item of paths) {
18388
+ const target = resolve19(root, item);
18389
+ if (target !== root && !target.startsWith(root + sep12))
18390
+ return true;
18391
+ skillRoots.push(target.endsWith(`${sep12}SKILL.md`) ? dirname19(target) : target);
18392
+ }
18393
+ }
18394
+ return skillRoots.some((skillRoot) => path === skillRoot || path.startsWith(skillRoot + sep12) || skillRoot.startsWith(path + sep12));
18395
+ } catch {
18396
+ return true;
18397
+ }
18398
+ }
18399
+
18273
18400
  // src/lib/agent-integration.ts
18274
18401
  import { createHash as createHash27, randomUUID as randomUUID4 } from "crypto";
18275
18402
  import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
18276
- import { existsSync as existsSync9, lstatSync as lstatSync27, statSync as statSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync18, readdirSync as readdirSync10, opendirSync as opendirSync4, readlinkSync as readlinkSync7, realpathSync as realpathSync12, renameSync as renameSync4, rmdirSync as rmdirSync3, writeFileSync as writeFileSync6, unlinkSync as unlinkSync5, chmodSync, openSync as openSync17, closeSync as closeSync17, fsyncSync as fsyncSync7, fstatSync as fstatSync17, readSync as readSync12, constants as constants18, linkSync as linkSync3 } from "fs";
18277
- import { basename as basename11, dirname as dirname21, join as join30, resolve as resolve22, relative as relative9, isAbsolute as isAbsolute24, sep as sep13 } from "path";
18403
+ import { existsSync as existsSync9, lstatSync as lstatSync28, statSync as statSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync19, readdirSync as readdirSync10, opendirSync as opendirSync4, readlinkSync as readlinkSync7, realpathSync as realpathSync12, renameSync as renameSync4, rmdirSync as rmdirSync3, writeFileSync as writeFileSync6, unlinkSync as unlinkSync5, chmodSync, openSync as openSync17, closeSync as closeSync17, fsyncSync as fsyncSync7, fstatSync as fstatSync17, readSync as readSync12, constants as constants18, linkSync as linkSync3 } from "fs";
18404
+ import { basename as basename11, dirname as dirname22, join as join31, resolve as resolve23, relative as relative9, isAbsolute as isAbsolute24, sep as sep14 } from "path";
18278
18405
  import { homedir as homedir2 } from "os";
18279
18406
 
18280
18407
  // src/lib/managed-policy.ts
18281
- import { closeSync as closeSync13, constants as constants13, fstatSync as fstatSync13, lstatSync as lstatSync21, openSync as openSync13, readSync as readSync9 } from "fs";
18282
- import { join as join26 } from "path";
18408
+ import { closeSync as closeSync13, constants as constants13, fstatSync as fstatSync13, lstatSync as lstatSync22, openSync as openSync13, readSync as readSync9 } from "fs";
18409
+ import { join as join27 } from "path";
18283
18410
  function refusal(reason2 = "is unreadable") {
18284
18411
  return new SkillSelectionError("INVALID_AGENT_POLICY", `The Skills agent loading policy ${reason2}; refusing legacy fallback.`);
18285
18412
  }
@@ -18310,10 +18437,10 @@ function serializeManagedSkillPolicy(value) {
18310
18437
  return text2;
18311
18438
  }
18312
18439
  function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
18313
- const path = join26(dataDir2, "agent-policy.json");
18440
+ const path = join27(dataDir2, "agent-policy.json");
18314
18441
  let descriptor, observed = false;
18315
18442
  try {
18316
- const stat2 = lstatSync21(path);
18443
+ const stat2 = lstatSync22(path);
18317
18444
  observed = true;
18318
18445
  if (!stat2.isFile() || stat2.isSymbolicLink())
18319
18446
  throw refusal();
@@ -18339,7 +18466,7 @@ function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
18339
18466
  return { text: text2, value: parseManagedSkillPolicy(text2) };
18340
18467
  } catch (error) {
18341
18468
  if (error.code === "ENOENT" && !observed) {
18342
- if (lstatSync21(path, { throwIfNoEntry: false }) === undefined)
18469
+ if (lstatSync22(path, { throwIfNoEntry: false }) === undefined)
18343
18470
  return null;
18344
18471
  }
18345
18472
  if (error instanceof SkillSelectionError)
@@ -18359,8 +18486,8 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
18359
18486
 
18360
18487
  // src/lib/agent-bridge.ts
18361
18488
  import { createHash as createHash23 } from "crypto";
18362
- import { lstatSync as lstatSync22, readFileSync as readFileSync16, readdirSync as readdirSync9 } from "fs";
18363
- import { join as join27 } from "path";
18489
+ import { lstatSync as lstatSync23, readFileSync as readFileSync17, readdirSync as readdirSync9 } from "fs";
18490
+ import { join as join28 } from "path";
18364
18491
  var CLI_BRIDGE_NAME = "skills-cli";
18365
18492
  var CLI_BRIDGE_VERSION = 1;
18366
18493
  var markdown = `---
@@ -18387,13 +18514,13 @@ function isOwnedCliBridge(path, expectedPaths) {
18387
18514
  if (!expectedPaths.includes(path))
18388
18515
  return false;
18389
18516
  try {
18390
- if (!lstatSync22(path).isDirectory())
18517
+ if (!lstatSync23(path).isDirectory())
18391
18518
  return false;
18392
18519
  if (JSON.stringify(readdirSync9(path).sort()) !== JSON.stringify(Object.keys(CLI_BRIDGE_FILES).sort()))
18393
18520
  return false;
18394
18521
  return Object.entries(CLI_BRIDGE_FILES).every(([name, content]) => {
18395
- const file = join27(path, name), stat2 = lstatSync22(file);
18396
- return stat2.isFile() && !stat2.isSymbolicLink() && stat2.size === Buffer.byteLength(content) && readFileSync16(file, "utf8") === content;
18522
+ const file = join28(path, name), stat2 = lstatSync23(file);
18523
+ return stat2.isFile() && !stat2.isSymbolicLink() && stat2.size === Buffer.byteLength(content) && readFileSync17(file, "utf8") === content;
18397
18524
  });
18398
18525
  } catch {
18399
18526
  return false;
@@ -18670,8 +18797,8 @@ function disableCodexBundledSkills(text2) {
18670
18797
 
18671
18798
  // src/lib/agent-sumi.ts
18672
18799
  import { spawnSync as spawnSync4 } from "child_process";
18673
- import { accessSync as accessSync2, constants as constants14, existsSync as existsSync8, lstatSync as lstatSync23, realpathSync as realpathSync10, statSync as statSync10 } from "fs";
18674
- import { delimiter as delimiter2, isAbsolute as isAbsolute20, join as join28, resolve as resolve19 } from "path";
18800
+ import { accessSync as accessSync2, constants as constants14, existsSync as existsSync8, lstatSync as lstatSync24, realpathSync as realpathSync10, statSync as statSync10 } from "fs";
18801
+ import { delimiter as delimiter2, isAbsolute as isAbsolute20, join as join29, resolve as resolve20 } from "path";
18675
18802
 
18676
18803
  // src/lib/hook-diagnostics.ts
18677
18804
  var CONTEXT_ERROR_CODES = new Set([
@@ -18760,7 +18887,7 @@ function recordWithKeys(value, keys) {
18760
18887
  return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === keys.length && keys.every((key2) => Object.hasOwn(value, key2));
18761
18888
  }
18762
18889
  function absolutePath3(value) {
18763
- return typeof value === "string" && value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute20(value) && resolve19(value) === value;
18890
+ return typeof value === "string" && value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute20(value) && resolve20(value) === value;
18764
18891
  }
18765
18892
  function absoluteInput(value) {
18766
18893
  return value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute20(value);
@@ -18771,7 +18898,7 @@ function resolveSumiPathsCommand(pathValue) {
18771
18898
  for (const directory2 of pathValue.split(delimiter2)) {
18772
18899
  if (!directory2 || /[\x00-\x1f\x7f]/.test(directory2))
18773
18900
  continue;
18774
- const candidate = resolve19(directory2, "sumi-paths");
18901
+ const candidate = resolve20(directory2, "sumi-paths");
18775
18902
  try {
18776
18903
  accessSync2(candidate, constants14.X_OK);
18777
18904
  if (statSync10(candidate).isFile())
@@ -18783,7 +18910,7 @@ function resolveSumiPathsCommand(pathValue) {
18783
18910
  function validPathPlan(value, home, cwd) {
18784
18911
  if (!recordWithKeys(value, ["schemaVersion", "kind", "home", "cwd", "roots", "legacyRoots", "configFiles", "skillRoots"]))
18785
18912
  return false;
18786
- if (value.schemaVersion !== 1 || value.kind !== "sumi-paths" || value.home !== resolve19(home) || value.cwd !== resolve19(cwd))
18913
+ if (value.schemaVersion !== 1 || value.kind !== "sumi-paths" || value.home !== resolve20(home) || value.cwd !== resolve20(cwd))
18787
18914
  return false;
18788
18915
  if (!recordWithKeys(value.roots, SUMI_PATH_KEYS) || !recordWithKeys(value.legacyRoots, SUMI_PATH_KEYS))
18789
18916
  return false;
@@ -18805,11 +18932,11 @@ function validPathPlan(value, home, cwd) {
18805
18932
  const legacyConfigRoot = value.legacyRoots.config;
18806
18933
  if (typeof configRoot !== "string" || legacyConfigRoot !== null && typeof legacyConfigRoot !== "string")
18807
18934
  return false;
18808
- if (value.configFiles.canonical !== join28(configRoot, "sumi.json") || value.skillRoots.canonical !== join28(configRoot, "skills"))
18935
+ if (value.configFiles.canonical !== join29(configRoot, "sumi.json") || value.skillRoots.canonical !== join29(configRoot, "skills"))
18809
18936
  return false;
18810
18937
  if (legacyConfigRoot === null)
18811
18938
  return value.configFiles.legacy === null && value.skillRoots.legacy === null;
18812
- return value.configFiles.legacy === join28(legacyConfigRoot, "sumi.json") && value.skillRoots.legacy === join28(legacyConfigRoot, "skills");
18939
+ return value.configFiles.legacy === join29(legacyConfigRoot, "sumi.json") && value.skillRoots.legacy === join29(legacyConfigRoot, "skills");
18813
18940
  }
18814
18941
  function sumiPathPlan(home, cwd = process.cwd(), env = process.env) {
18815
18942
  for (const key2 of ["SUMI_CONFIG", "SUMI_CONFIG_CONTENT"])
@@ -18827,8 +18954,8 @@ function sumiPathPlan(home, cwd = process.cwd(), env = process.env) {
18827
18954
  childEnv[key2] = env[key2];
18828
18955
  let child2;
18829
18956
  try {
18830
- child2 = spawnSync4(executable2, ["--json", "--home", resolve19(home), "--cwd", resolve19(cwd)], {
18831
- cwd: resolve19(cwd),
18957
+ child2 = spawnSync4(executable2, ["--json", "--home", resolve20(home), "--cwd", resolve20(cwd)], {
18958
+ cwd: resolve20(cwd),
18832
18959
  env: childEnv,
18833
18960
  encoding: "buffer",
18834
18961
  timeout: 3000,
@@ -18860,7 +18987,7 @@ function selectedConfigDirectory(plan) {
18860
18987
  const canonical9 = plan.roots.config, legacy = plan.legacyRoots.config;
18861
18988
  if (legacy === null)
18862
18989
  return existsSync8(canonical9) ? realpathSync10(canonical9) : canonical9;
18863
- const canonicalStat = lstatSync23(canonical9, { throwIfNoEntry: false }), legacyStat = lstatSync23(legacy, { throwIfNoEntry: false });
18990
+ const canonicalStat = lstatSync24(canonical9, { throwIfNoEntry: false }), legacyStat = lstatSync24(legacy, { throwIfNoEntry: false });
18864
18991
  if (canonicalStat && legacyStat && realpathSync10(canonical9) !== realpathSync10(legacy))
18865
18992
  throw new Error("Sumi config roots conflict; complete native home adoption before Skills integration");
18866
18993
  const selected = canonicalStat ? canonical9 : legacyStat ? legacy : canonical9;
@@ -18876,13 +19003,13 @@ function sumiConfigDirectory(home, env = process.env, cwd = process.cwd()) {
18876
19003
  }
18877
19004
  function sumiSkillRoots(home, cwd = process.cwd(), env = process.env) {
18878
19005
  const plan = sumiPathPlan(home, cwd, env);
18879
- return [join28(selectedConfigDirectory(plan), "skills")];
19006
+ return [join29(selectedConfigDirectory(plan), "skills")];
18880
19007
  }
18881
19008
  function sumiBridgeRoot(home, cwd = process.cwd(), env = process.env) {
18882
19009
  return sumiSkillRoots(home, cwd, env)[0];
18883
19010
  }
18884
19011
  function sumiConfigPath(home, cwd = process.cwd(), env = process.env) {
18885
- return join28(sumiConfigDirectory(home, env, cwd), "sumi.json");
19012
+ return join29(sumiConfigDirectory(home, env, cwd), "sumi.json");
18886
19013
  }
18887
19014
  function renderSumiPlugin(command, profile) {
18888
19015
  return renderSumiPluginVersion(command, profile, 3);
@@ -18987,9 +19114,9 @@ var SUMI_SKILL_PERMISSIONS = [
18987
19114
  ];
18988
19115
 
18989
19116
  // src/lib/codex-hook-trust-files.ts
18990
- import { closeSync as closeSync14, constants as constants15, fstatSync as fstatSync14, fsyncSync as fsyncSync4, lstatSync as lstatSync24, openSync as openSync14, readlinkSync as readlinkSync5, readSync as readSync10, writeFileSync as writeFileSync5 } from "fs";
19117
+ import { closeSync as closeSync14, constants as constants15, fstatSync as fstatSync14, fsyncSync as fsyncSync4, lstatSync as lstatSync25, openSync as openSync14, readlinkSync as readlinkSync5, readSync as readSync10, writeFileSync as writeFileSync5 } from "fs";
18991
19118
  import { createHash as createHash24 } from "crypto";
18992
- import { dirname as dirname19, isAbsolute as isAbsolute21, resolve as resolve20 } from "path";
19119
+ import { dirname as dirname20, isAbsolute as isAbsolute21, resolve as resolve21 } from "path";
18993
19120
  function need8(value, code) {
18994
19121
  if (!value)
18995
19122
  throw new Error(`CODEX_HOOK_TRUST_${code}`);
@@ -18999,11 +19126,11 @@ function trustedAncestorOwner(uid2) {
18999
19126
  return BigInt(uid2) === 0n || BigInt(uid2) === BigInt(process.getuid());
19000
19127
  }
19001
19128
  function safeParents(file) {
19002
- for (let p = dirname19(file);; p = dirname19(p)) {
19003
- const s = lstatSync24(p);
19129
+ for (let p = dirname20(file);; p = dirname20(p)) {
19130
+ const s = lstatSync25(p);
19004
19131
  need8(s.isDirectory() && !s.isSymbolicLink() && trustedAncestorOwner(s.uid), "UNSAFE_PARENT");
19005
19132
  need8((s.mode & 18) === 0 || s.uid === 0 && (s.mode & 512) !== 0, "UNSAFE_PARENT");
19006
- if (dirname19(p) === p)
19133
+ if (dirname20(p) === p)
19007
19134
  break;
19008
19135
  }
19009
19136
  }
@@ -19018,12 +19145,12 @@ function directAliasTarget(link) {
19018
19145
  const names = parts.slice(first);
19019
19146
  if (!names.length || names.some((part) => part === "" || part === "." || part === ".."))
19020
19147
  return;
19021
- return resolve20(dirname19(link), text2);
19148
+ return resolve21(dirname20(link), text2);
19022
19149
  }
19023
19150
  function snapshot(file, privateFile = false, options = {}) {
19024
19151
  const readOnlyPackage = options.readOnlyPackage === true && !privateFile;
19025
19152
  safeParents(file);
19026
- const initial = lstatSync24(file, { bigint: true });
19153
+ const initial = lstatSync25(file, { bigint: true });
19027
19154
  need8(initial.isFile() && initial.uid === BigInt(process.getuid()) && (initial.nlink === 1n || readOnlyPackage && initial.nlink > 1n) && (initial.mode & 0o022n) === 0n && (!privateFile || (initial.mode & 0o777n) === 0o600n), "UNSAFE_FILE");
19028
19155
  need8(initial.size <= 8n * 1024n * 1024n, "FILE_BOUND");
19029
19156
  const fd = openSync14(file, constants15.O_RDONLY | constants15.O_NOFOLLOW | constants15.O_NONBLOCK);
@@ -19038,7 +19165,7 @@ function snapshot(file, privateFile = false, options = {}) {
19038
19165
  break;
19039
19166
  length += n2;
19040
19167
  }
19041
- need8(length === Number(before.size) && sameStat(before, fstatSync14(fd, { bigint: true })) && sameStat(before, lstatSync24(file, { bigint: true })), "INPUT_CHANGED");
19168
+ need8(length === Number(before.size) && sameStat(before, fstatSync14(fd, { bigint: true })) && sameStat(before, lstatSync25(file, { bigint: true })), "INPUT_CHANGED");
19042
19169
  const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
19043
19170
  return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash24("sha256").update(content).digest("hex") };
19044
19171
  } finally {
@@ -19052,7 +19179,7 @@ function unchanged2(before) {
19052
19179
 
19053
19180
  // src/cli/commands/runtime-launcher.ts
19054
19181
  import { createHash as createHash25 } from "crypto";
19055
- import { closeSync as closeSync15, constants as constants16, fchmodSync as fchmodSync2, fstatSync as fstatSync15, fsyncSync as fsyncSync5, lstatSync as lstatSync25, openSync as openSync15, readFileSync as readFileSync17, readlinkSync as readlinkSync6, realpathSync as realpathSync11, symlinkSync, writeSync as writeSync3 } from "fs";
19182
+ import { closeSync as closeSync15, constants as constants16, fchmodSync as fchmodSync2, fstatSync as fstatSync15, fsyncSync as fsyncSync5, lstatSync as lstatSync26, openSync as openSync15, readFileSync as readFileSync18, readlinkSync as readlinkSync6, realpathSync as realpathSync11, symlinkSync, writeSync as writeSync3 } from "fs";
19056
19183
  import { isAbsolute as isAbsolute22 } from "path";
19057
19184
 
19058
19185
  // src/server/launcher-environment-policy.ts
@@ -19259,9 +19386,9 @@ function parsePinnedLauncher(text2) {
19259
19386
  var PINNED_LAUNCHER_MAX_BYTES = 16 * 1024;
19260
19387
  function resolveLauncherCommand(path) {
19261
19388
  const physical = realpathSync11(path);
19262
- const stat2 = lstatSync25(physical);
19389
+ const stat2 = lstatSync26(physical);
19263
19390
  if (stat2.isFile() && stat2.size <= PINNED_LAUNCHER_MAX_BYTES) {
19264
- const text2 = readFileSync17(physical, "utf8");
19391
+ const text2 = readFileSync18(physical, "utf8");
19265
19392
  const binding = parsePinnedLauncher(text2);
19266
19393
  if (binding) {
19267
19394
  let entry;
@@ -19281,7 +19408,7 @@ function assertPinnedLauncherRuntimeSafe(runtime) {
19281
19408
  assertLauncherPath(runtime, "LAUNCHER_RUNTIME_PATH_INVALID");
19282
19409
  let stat2;
19283
19410
  try {
19284
- stat2 = lstatSync25(runtime);
19411
+ stat2 = lstatSync26(runtime);
19285
19412
  } catch {
19286
19413
  throw new Error("LAUNCHER_RUNTIME_UNSAFE");
19287
19414
  }
@@ -19293,8 +19420,8 @@ function assertPinnedLauncherRuntimeSafe(runtime) {
19293
19420
 
19294
19421
  // src/lib/retained-review-artifacts.ts
19295
19422
  import { createHash as createHash26 } from "crypto";
19296
- import { constants as constants17, closeSync as closeSync16, fstatSync as fstatSync16, fsyncSync as fsyncSync6, lstatSync as lstatSync26, mkdirSync as mkdirSync7, openSync as openSync16, readSync as readSync11, writeSync as writeSync4 } from "fs";
19297
- import { dirname as dirname20, isAbsolute as isAbsolute23, join as join29, resolve as resolve21, sep as sep12 } from "path";
19423
+ import { constants as constants17, closeSync as closeSync16, fstatSync as fstatSync16, fsyncSync as fsyncSync6, lstatSync as lstatSync27, mkdirSync as mkdirSync7, openSync as openSync16, readSync as readSync11, writeSync as writeSync4 } from "fs";
19424
+ import { dirname as dirname21, isAbsolute as isAbsolute23, join as join30, resolve as resolve22, sep as sep13 } from "path";
19298
19425
  var sha3 = (bytes) => createHash26("sha256").update(bytes).digest("hex");
19299
19426
  var hex = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
19300
19427
  var refuse5 = () => {
@@ -19308,19 +19435,19 @@ function keys(value, names) {
19308
19435
  return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === names.length && names.every((name) => Object.hasOwn(value, name)));
19309
19436
  }
19310
19437
  function safe4(path) {
19311
- need9(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute23(path) && resolve21(path) === path && !/[\x00-\x1f\x7f]/.test(path));
19312
- for (let cursor = path;; cursor = dirname20(cursor)) {
19313
- const stat2 = lstatSync26(cursor, { throwIfNoEntry: false });
19438
+ need9(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute23(path) && resolve22(path) === path && !/[\x00-\x1f\x7f]/.test(path));
19439
+ for (let cursor = path;; cursor = dirname21(cursor)) {
19440
+ const stat2 = lstatSync27(cursor, { throwIfNoEntry: false });
19314
19441
  need9(!stat2?.isSymbolicLink());
19315
19442
  if (cursor !== path && stat2)
19316
19443
  need9(stat2.isDirectory());
19317
- if (dirname20(cursor) === cursor)
19444
+ if (dirname21(cursor) === cursor)
19318
19445
  break;
19319
19446
  }
19320
19447
  }
19321
19448
  function readExact(path) {
19322
19449
  safe4(path);
19323
- const before = lstatSync26(path, { throwIfNoEntry: false });
19450
+ const before = lstatSync27(path, { throwIfNoEntry: false });
19324
19451
  need9(before?.isFile() && before.nlink === 1 && before.size <= AGENT_POLICY_LIMITS.discoveryRawSourceBytes && (before.mode & 63) === 0 && (process.getuid === undefined || before.uid === process.getuid()));
19325
19452
  const fd = openSync16(path, constants17.O_RDONLY | constants17.O_NOFOLLOW | constants17.O_NONBLOCK);
19326
19453
  try {
@@ -19334,7 +19461,7 @@ function readExact(path) {
19334
19461
  offset += count;
19335
19462
  }
19336
19463
  need9(readSync11(fd, Buffer.alloc(1), 0, 1, null) === 0);
19337
- const after = fstatSync16(fd), current = lstatSync26(path, { throwIfNoEntry: false });
19464
+ const after = fstatSync16(fd), current = lstatSync27(path, { throwIfNoEntry: false });
19338
19465
  safe4(path);
19339
19466
  need9(current && opened.dev === current.dev && opened.ino === current.ino && opened.mode === current.mode && opened.uid === current.uid && opened.size === current.size && opened.mtimeMs === current.mtimeMs && opened.ctimeMs === current.ctimeMs && current.nlink === 1 && opened.mode === after.mode && opened.uid === after.uid && after.nlink === 1 && opened.size === after.size && opened.mtimeMs === after.mtimeMs && opened.ctimeMs === after.ctimeMs);
19340
19467
  const text2 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
@@ -19360,24 +19487,24 @@ function verifyReviewArtifactOriginal(path, expectedSha256) {
19360
19487
  need9(hex(expectedSha256) && sha3(readExact(path)) === expectedSha256);
19361
19488
  }
19362
19489
  function retainedArtifactPath(metadata) {
19363
- return join29(metadata.storeRoot, "agent-discovery", "artifacts", metadata.sha256, "catalog.json");
19490
+ return join30(metadata.storeRoot, "agent-discovery", "artifacts", metadata.sha256, "catalog.json");
19364
19491
  }
19365
19492
  function validRetainedArtifact(value) {
19366
- return keys(value, ["version", "kind", "storeRoot", "originalPath", "sha256", "catalogSha256"]) && value.version === 1 && value.kind === "codex-native-catalog" && hex(value.sha256) && hex(value.catalogSha256) && [value.storeRoot, value.originalPath].every((path) => typeof path === "string" && isAbsolute23(path) && resolve21(path) === path && path.length <= AGENT_POLICY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(path));
19493
+ return keys(value, ["version", "kind", "storeRoot", "originalPath", "sha256", "catalogSha256"]) && value.version === 1 && value.kind === "codex-native-catalog" && hex(value.sha256) && hex(value.catalogSha256) && [value.storeRoot, value.originalPath].every((path) => typeof path === "string" && isAbsolute23(path) && resolve22(path) === path && path.length <= AGENT_POLICY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(path));
19367
19494
  }
19368
19495
  function privateStore(storeRoot) {
19369
19496
  safe4(storeRoot);
19370
- const stat2 = lstatSync26(storeRoot, { throwIfNoEntry: false });
19497
+ const stat2 = lstatSync27(storeRoot, { throwIfNoEntry: false });
19371
19498
  need9(!stat2 || stat2.isDirectory() && (stat2.mode & 18) === 0 && (process.getuid === undefined || stat2.uid === process.getuid()));
19372
19499
  }
19373
19500
  function verifyRetainedReviewArtifact(source, storeRoot) {
19374
19501
  const artifact = source.reviewArtifact;
19375
- need9(validRetainedArtifact(artifact) && source.hashMode === "bytes" && source.sha256 === artifact.sha256 && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && source.path === retainedArtifactPath(artifact) && (storeRoot === undefined || artifact.storeRoot === resolve21(storeRoot)));
19502
+ need9(validRetainedArtifact(artifact) && source.hashMode === "bytes" && source.sha256 === artifact.sha256 && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && source.path === retainedArtifactPath(artifact) && (storeRoot === undefined || artifact.storeRoot === resolve22(storeRoot)));
19376
19503
  privateStore(artifact.storeRoot);
19377
19504
  const text2 = readExact(source.path);
19378
19505
  need9(sha3(text2) === artifact.sha256 && catalogDigest(text2) === artifact.catalogSha256);
19379
- for (let parent = dirname20(source.path);parent !== artifact.storeRoot; parent = dirname20(parent)) {
19380
- const stat2 = lstatSync26(parent);
19506
+ for (let parent = dirname21(source.path);parent !== artifact.storeRoot; parent = dirname21(parent)) {
19507
+ const stat2 = lstatSync27(parent);
19381
19508
  need9(stat2.isDirectory() && (stat2.mode & 63) === 0 && (process.getuid === undefined || stat2.uid === process.getuid()));
19382
19509
  }
19383
19510
  }
@@ -19385,7 +19512,7 @@ function planReviewArtifactRetention(binding, artifacts, dataDir2, home) {
19385
19512
  if (artifacts === undefined)
19386
19513
  return { binding, changes: [], observed: [] };
19387
19514
  need9(binding.agent === "codex" && binding.method === "reviewed" && Array.isArray(artifacts) && artifacts.length > 0 && artifacts.length <= 32);
19388
- privateStore(resolve21(dataDir2));
19515
+ privateStore(resolve22(dataDir2));
19389
19516
  const paths = new Set;
19390
19517
  const changes = [], observed = [];
19391
19518
  let totalBytes = 0;
@@ -19394,19 +19521,19 @@ function planReviewArtifactRetention(binding, artifacts, dataDir2, home) {
19394
19521
  need9(keys(declaration, ["kind", "path", "sha256"]) && declaration.kind === "codex-native-catalog" && hex(declaration.sha256) && !paths.has(declaration.path));
19395
19522
  paths.add(declaration.path);
19396
19523
  safe4(declaration.path);
19397
- const liveRoots = [join29(home, ".codex"), ...binding.roots, ...(binding.directories ?? []).map((item) => item.path)];
19398
- need9(!liveRoots.some((root) => declaration.path === root || declaration.path.startsWith(root + sep12)));
19524
+ const liveRoots = [join30(home, ".codex"), ...binding.roots, ...(binding.directories ?? []).map((item) => item.path)];
19525
+ need9(!liveRoots.some((root) => declaration.path === root || declaration.path.startsWith(root + sep13)));
19399
19526
  const matches = sources.filter((source2) => source2.path === declaration.path);
19400
19527
  need9(matches.length === 1 && matches[0].sha256 === declaration.sha256 && matches[0].reviewArtifact === undefined && [undefined, "bytes"].includes(matches[0].hashMode) && matches[0].format === undefined && matches[0].fields === undefined && matches[0].managedPlugins === undefined);
19401
19528
  const text2 = readExact(declaration.path);
19402
19529
  need9(sha3(text2) === declaration.sha256);
19403
19530
  totalBytes += Buffer.byteLength(text2);
19404
19531
  need9(totalBytes <= AGENT_POLICY_LIMITS.discoveryRawTotalBytes);
19405
- const metadata = { version: 1, kind: declaration.kind, storeRoot: resolve21(dataDir2), originalPath: declaration.path, sha256: declaration.sha256, catalogSha256: catalogDigest(text2) };
19532
+ const metadata = { version: 1, kind: declaration.kind, storeRoot: resolve22(dataDir2), originalPath: declaration.path, sha256: declaration.sha256, catalogSha256: catalogDigest(text2) };
19406
19533
  const path = retainedArtifactPath(metadata);
19407
19534
  safe4(path);
19408
19535
  const source = { path, sha256: metadata.sha256, hashMode: "bytes", reviewArtifact: metadata };
19409
- if (lstatSync26(path, { throwIfNoEntry: false }))
19536
+ if (lstatSync27(path, { throwIfNoEntry: false }))
19410
19537
  verifyRetainedReviewArtifact(source, dataDir2);
19411
19538
  else if (!changes.some((change) => change.path === path))
19412
19539
  changes.push({ path, before: null, after: text2, artifact: metadata });
@@ -19420,14 +19547,14 @@ function writeReviewArtifactExclusive(change) {
19420
19547
  need9(change.before === null && validRetainedArtifact(change.artifact) && change.path === retainedArtifactPath(change.artifact) && sha3(change.after) === change.artifact.sha256 && catalogDigest(change.after) === change.artifact.catalogSha256);
19421
19548
  privateStore(change.artifact.storeRoot);
19422
19549
  const missing2 = [];
19423
- for (let parent = dirname20(change.path);!lstatSync26(parent, { throwIfNoEntry: false }); parent = dirname20(parent))
19550
+ for (let parent = dirname21(change.path);!lstatSync27(parent, { throwIfNoEntry: false }); parent = dirname21(parent))
19424
19551
  missing2.push(parent);
19425
19552
  for (const parent of missing2.reverse())
19426
19553
  mkdirSync7(parent, { mode: 448 });
19427
19554
  safe4(change.path);
19428
19555
  privateStore(change.artifact.storeRoot);
19429
- for (let parent = dirname20(change.path);parent !== change.artifact.storeRoot; parent = dirname20(parent)) {
19430
- const stat2 = lstatSync26(parent);
19556
+ for (let parent = dirname21(change.path);parent !== change.artifact.storeRoot; parent = dirname21(parent)) {
19557
+ const stat2 = lstatSync27(parent);
19431
19558
  need9(stat2.isDirectory() && (stat2.mode & 63) === 0 && (process.getuid === undefined || stat2.uid === process.getuid()));
19432
19559
  }
19433
19560
  const fd = openSync16(change.path, constants17.O_WRONLY | constants17.O_CREAT | constants17.O_EXCL | constants17.O_NOFOLLOW, 384);
@@ -19444,7 +19571,7 @@ function writeReviewArtifactExclusive(change) {
19444
19571
  closeSync16(fd);
19445
19572
  }
19446
19573
  need9(readExact(change.path) === change.after);
19447
- const directory2 = openSync16(dirname20(change.path), constants17.O_RDONLY | constants17.O_DIRECTORY | constants17.O_NOFOLLOW);
19574
+ const directory2 = openSync16(dirname21(change.path), constants17.O_RDONLY | constants17.O_DIRECTORY | constants17.O_NOFOLLOW);
19448
19575
  try {
19449
19576
  fsyncSync6(directory2);
19450
19577
  } finally {
@@ -19461,7 +19588,7 @@ function nativeAgentRoot(home, agent, cwd = process.cwd()) {
19461
19588
  const root = AGENT_ADAPTERS[agent].root;
19462
19589
  if (typeof root !== "string")
19463
19590
  throw new Error(`Native skill root is unavailable for ${agent}`);
19464
- return join30(home, root);
19591
+ return join31(home, root);
19465
19592
  }
19466
19593
  function nativeAgentConfig(home, agent, cwd = process.cwd()) {
19467
19594
  if (agent === "sumi")
@@ -19469,7 +19596,7 @@ function nativeAgentConfig(home, agent, cwd = process.cwd()) {
19469
19596
  const config = AGENT_ADAPTERS[agent].config;
19470
19597
  if (typeof config !== "string")
19471
19598
  throw new Error(`Native configuration path is unavailable for ${agent}`);
19472
- return join30(home, config);
19599
+ return join31(home, config);
19473
19600
  }
19474
19601
  function hasTrustedDarwinAcl(fd, allowDenyOnly = false) {
19475
19602
  try {
@@ -19521,11 +19648,11 @@ function isSystemRootAlias(path) {
19521
19648
  const identity5 = (stat2) => `${stat2.dev}:${stat2.ino}:${stat2.ctimeNs}:${stat2.uid}:${stat2.mode}`;
19522
19649
  const directoryIdentity = (stat2) => `${stat2.dev}:${stat2.ino}:${stat2.uid}:${stat2.mode}`;
19523
19650
  try {
19524
- const before = lstatSync27(path, { bigint: true });
19651
+ const before = lstatSync28(path, { bigint: true });
19525
19652
  if (!before.isSymbolicLink() || before.uid !== 0n || readlinkSync7(path) !== link)
19526
19653
  return false;
19527
19654
  const directories = ["/", "/private", target].map((directory2) => {
19528
- const stat2 = lstatSync27(directory2, { bigint: true });
19655
+ const stat2 = lstatSync28(directory2, { bigint: true });
19529
19656
  if (!stat2.isDirectory() || stat2.uid !== 0n)
19530
19657
  throw new Error("Untrusted system alias directory");
19531
19658
  if (directory2 !== "/private/tmp") {
@@ -19542,25 +19669,25 @@ function isSystemRootAlias(path) {
19542
19669
  }
19543
19670
  return { directory: directory2, identity: directoryIdentity(stat2) };
19544
19671
  });
19545
- return realpathSync12(path) === target && readlinkSync7(path) === link && identity5(lstatSync27(path, { bigint: true })) === identity5(before) && directories.every((item) => directoryIdentity(lstatSync27(item.directory, { bigint: true })) === item.identity);
19672
+ return realpathSync12(path) === target && readlinkSync7(path) === link && identity5(lstatSync28(path, { bigint: true })) === identity5(before) && directories.every((item) => directoryIdentity(lstatSync28(item.directory, { bigint: true })) === item.identity);
19546
19673
  } catch {
19547
19674
  return false;
19548
19675
  }
19549
19676
  }
19550
19677
  function canonicalSystemPath(path) {
19551
- const absolute = resolve22(path), root = `/${absolute.split(sep13)[1]}`;
19552
- if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync27(root, { throwIfNoEntry: false })?.isSymbolicLink())
19678
+ const absolute = resolve23(path), root = `/${absolute.split(sep14)[1]}`;
19679
+ if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync28(root, { throwIfNoEntry: false })?.isSymbolicLink())
19553
19680
  return absolute;
19554
19681
  if (!isSystemRootAlias(root))
19555
19682
  throw new Error(`Refusing symlink path: ${root}`);
19556
19683
  return `/private${absolute}`;
19557
19684
  }
19558
19685
  function assertSafePath(path) {
19559
- let cursor = resolve22(path);
19686
+ let cursor = resolve23(path);
19560
19687
  while (true) {
19561
- if (lstatSync27(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
19688
+ if (lstatSync28(cursor, { throwIfNoEntry: false })?.isSymbolicLink() && !isSystemRootAlias(cursor))
19562
19689
  throw new Error(`Refusing symlink path: ${cursor}`);
19563
- const parent = dirname21(cursor);
19690
+ const parent = dirname22(cursor);
19564
19691
  if (parent === cursor)
19565
19692
  return;
19566
19693
  cursor = parent;
@@ -19568,17 +19695,17 @@ function assertSafePath(path) {
19568
19695
  }
19569
19696
  function readRootAlias(home, agent) {
19570
19697
  assertSafePath(home);
19571
- const realHome = realpathSync12(home), alias = join30(realHome, `.${agent}`);
19572
- const stat2 = lstatSync27(alias, { throwIfNoEntry: false, bigint: true });
19698
+ const realHome = realpathSync12(home), alias = join31(realHome, `.${agent}`);
19699
+ const stat2 = lstatSync28(alias, { throwIfNoEntry: false, bigint: true });
19573
19700
  if (!stat2?.isSymbolicLink())
19574
19701
  throw new Error(`Agent root alias changed after planning: ${alias}`);
19575
- const link = readlinkSync7(alias), targetPath = resolve22(dirname21(alias), link);
19702
+ const link = readlinkSync7(alias), targetPath = resolve23(dirname22(alias), link);
19576
19703
  assertSafePath(targetPath);
19577
- const targetStat = lstatSync27(targetPath, { throwIfNoEntry: false });
19704
+ const targetStat = lstatSync28(targetPath, { throwIfNoEntry: false });
19578
19705
  if (!targetStat?.isDirectory())
19579
19706
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
19580
19707
  const target = realpathSync12(targetPath), inside = relative9(realHome, target);
19581
- if (!inside || inside === ".." || inside.startsWith(`..${sep13}`) || isAbsolute24(inside))
19708
+ if (!inside || inside === ".." || inside.startsWith(`..${sep14}`) || isAbsolute24(inside))
19582
19709
  throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
19583
19710
  return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat2.dev}:${stat2.ino}:${stat2.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
19584
19711
  }
@@ -19587,15 +19714,15 @@ function rootAliases(home, allowed = false) {
19587
19714
  return [];
19588
19715
  const result = [];
19589
19716
  for (const agent of ["claude", "codex"])
19590
- if (lstatSync27(join30(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
19717
+ if (lstatSync28(join31(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
19591
19718
  result.push(readRootAlias(home, agent));
19592
- if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep13) || other.target.startsWith(item.target + sep13))))
19719
+ if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep14) || other.target.startsWith(item.target + sep14))))
19593
19720
  throw new Error("Agent root alias targets must not overlap");
19594
19721
  return result;
19595
19722
  }
19596
19723
  function canonicalAgentPath(path, aliases) {
19597
- const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep13));
19598
- return binding ? join30(binding.target, relative9(binding.alias, absolute)) : absolute;
19724
+ const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep14));
19725
+ return binding ? join31(binding.target, relative9(binding.alias, absolute)) : absolute;
19599
19726
  }
19600
19727
  function disabledCodexSkillPaths(config, aliases) {
19601
19728
  const skills = config.skills;
@@ -19618,15 +19745,15 @@ function disabledCodexSkillPaths(config, aliases) {
19618
19745
  } catch {
19619
19746
  continue;
19620
19747
  }
19621
- if (!document2.endsWith(`${sep13}SKILL.md`))
19748
+ if (!document2.endsWith(`${sep14}SKILL.md`))
19622
19749
  continue;
19623
19750
  settings.set(document2, [...settings.get(document2) ?? [], entry.enabled === false]);
19624
19751
  }
19625
- return [...settings].filter(([, values]) => values.length === 1 && values[0] === true).map(([document2]) => dirname21(document2));
19752
+ return [...settings].filter(([, values]) => values.length === 1 && values[0] === true).map(([document2]) => dirname22(document2));
19626
19753
  }
19627
19754
  function recheckRootAliases(aliases) {
19628
19755
  for (const binding of aliases) {
19629
- if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join30(binding.home, `.${binding.agent}`))
19756
+ if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join31(binding.home, `.${binding.agent}`))
19630
19757
  throw new Error("Invalid agent root alias binding");
19631
19758
  const current = readRootAlias(binding.home, binding.agent);
19632
19759
  if (JSON.stringify(current) !== JSON.stringify(binding))
@@ -19635,11 +19762,11 @@ function recheckRootAliases(aliases) {
19635
19762
  }
19636
19763
  function nativeSourceDigest(path) {
19637
19764
  assertSafePath(path);
19638
- return lstatSync27(path, { throwIfNoEntry: false }) ? sha4(readNativeBytes(path, 1024 * 1024)) : null;
19765
+ return lstatSync28(path, { throwIfNoEntry: false }) ? sha4(readNativeBytes(path, 1024 * 1024)) : null;
19639
19766
  }
19640
19767
  function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
19641
19768
  assertSafePath(path);
19642
- const before = lstatSync27(path);
19769
+ const before = lstatSync28(path);
19643
19770
  if (!before.isFile() || before.size > maximum)
19644
19771
  throw new Error("Unsupported or oversized native skill file");
19645
19772
  const descriptor = openSync17(path, constants18.O_RDONLY | constants18.O_NOFOLLOW | constants18.O_NONBLOCK);
@@ -19655,7 +19782,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
19655
19782
  break;
19656
19783
  length += count;
19657
19784
  }
19658
- const after = fstatSync17(descriptor), current = lstatSync27(path);
19785
+ const after = fstatSync17(descriptor), current = lstatSync28(path);
19659
19786
  if (current.dev !== opened.dev || current.ino !== opened.ino || current.size !== after.size || current.mtimeMs !== after.mtimeMs || current.ctimeMs !== after.ctimeMs || length !== before.size || after.size !== before.size || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs)
19660
19787
  throw new Error("Native skill file changed while reading");
19661
19788
  return bytes.subarray(0, length);
@@ -19667,14 +19794,14 @@ function treeHash(root) {
19667
19794
  const hash3 = createHash27("sha256");
19668
19795
  let bytes = 0, files = 0;
19669
19796
  function visit2(path) {
19670
- const stat2 = lstatSync27(path);
19797
+ const stat2 = lstatSync28(path);
19671
19798
  if (stat2.isSymbolicLink())
19672
19799
  throw new Error(`Refusing symlink in skill: ${path}`);
19673
19800
  const rel = relative9(root, path);
19674
19801
  if (stat2.isDirectory()) {
19675
19802
  hash3.update(`d\x00${rel}\x00`);
19676
19803
  for (const name of readdirSync10(path).sort())
19677
- visit2(join30(path, name));
19804
+ visit2(join31(path, name));
19678
19805
  return;
19679
19806
  }
19680
19807
  if (!stat2.isFile())
@@ -19696,20 +19823,20 @@ function selectNativeMigrationTargets(inventory, manifest) {
19696
19823
  const identities = new Set;
19697
19824
  const selected = [];
19698
19825
  for (const target of manifest.targets) {
19699
- if (!isAbsolute24(target.projectRoot) || resolve22(target.projectRoot) !== target.projectRoot)
19826
+ if (!isAbsolute24(target.projectRoot) || resolve23(target.projectRoot) !== target.projectRoot)
19700
19827
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
19701
- const projectRoot = canonicalSystemPath(target.projectRoot), normalized2 = relative9(projectRoot, resolve22(projectRoot, target.path));
19702
- if (!existsSync9(projectRoot) || !lstatSync27(projectRoot).isDirectory())
19828
+ const projectRoot = canonicalSystemPath(target.projectRoot), normalized2 = relative9(projectRoot, resolve23(projectRoot, target.path));
19829
+ if (!existsSync9(projectRoot) || !lstatSync28(projectRoot).isDirectory())
19703
19830
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
19704
19831
  assertSafePath(projectRoot);
19705
- if (isAbsolute24(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized2 || normalized2 === ".." || normalized2.startsWith(`..${sep13}`) || normalized2 !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
19832
+ if (isAbsolute24(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized2 || normalized2 === ".." || normalized2.startsWith(`..${sep14}`) || normalized2 !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
19706
19833
  throw new Error(`Invalid native migration target: ${target.path}`);
19707
19834
  const identity5 = `${target.agent}\x00${canonicalSystemPath(projectRoot)}\x00${normalized2}`;
19708
19835
  if (identities.has(identity5))
19709
19836
  throw new Error(`Duplicate native migration target: ${target.path}`);
19710
19837
  identities.add(identity5);
19711
- const expected = canonicalSystemPath(resolve22(target.projectRoot, target.path));
19712
- const matches = inventory.filter((entry2) => entry2.agent === target.agent && resolve22(entry2.path) === expected);
19838
+ const expected = canonicalSystemPath(resolve23(target.projectRoot, target.path));
19839
+ const matches = inventory.filter((entry2) => entry2.agent === target.agent && resolve23(entry2.path) === expected);
19713
19840
  if (matches.length !== 1)
19714
19841
  throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
19715
19842
  const entry = matches[0];
@@ -19728,7 +19855,7 @@ function projectAncestorDirectories(projects) {
19728
19855
  if (depth >= 100)
19729
19856
  throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
19730
19857
  directories.add(path);
19731
- const parent = dirname21(path);
19858
+ const parent = dirname22(path);
19732
19859
  if (parent === path)
19733
19860
  break;
19734
19861
  path = parent;
@@ -19737,17 +19864,17 @@ function projectAncestorDirectories(projects) {
19737
19864
  return [...directories];
19738
19865
  }
19739
19866
  function isCodexSkillDiscoveryInput(path) {
19740
- for (let cursor = canonicalSystemPath(path);cursor !== dirname21(cursor); cursor = dirname21(cursor)) {
19741
- if (ROOTS.some(([agent, root]) => agent === "codex" && cursor.endsWith(join30(sep13, root))))
19867
+ for (let cursor = canonicalSystemPath(path);cursor !== dirname22(cursor); cursor = dirname22(cursor)) {
19868
+ if (ROOTS.some(([agent, root]) => agent === "codex" && cursor.endsWith(join31(sep14, root))))
19742
19869
  return true;
19743
19870
  }
19744
19871
  return false;
19745
19872
  }
19746
19873
  function inventoryNativeSkills(home = homedir2(), options = {}) {
19747
19874
  home = canonicalSystemPath(home);
19748
- if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute24(options.reviewedCacheAlias) || resolve22(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
19875
+ if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute24(options.reviewedCacheAlias) || resolve23(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
19749
19876
  throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
19750
- if (options.disabledVendorPaths?.some((path) => !isAbsolute24(path) || resolve22(path) !== path))
19877
+ if (options.disabledVendorPaths?.some((path) => !isAbsolute24(path) || resolve23(path) !== path))
19751
19878
  throw new Error("Disabled vendor paths must be exact absolute paths");
19752
19879
  const reviewedCacheAlias = options.reviewedCacheAlias === undefined ? undefined : canonicalSystemPath(options.reviewedCacheAlias);
19753
19880
  const disabledVendorPaths = new Set((options.disabledVendorPaths ?? []).map(canonicalSystemPath));
@@ -19755,25 +19882,25 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
19755
19882
  const selectedAgents = options.agents ? new Set(options.agents) : undefined;
19756
19883
  const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
19757
19884
  const rootDefinitions = selectedAgents ? ROOTS.filter(([agent, path]) => agent !== "sumi" && selectedAgents.has(agent) || selectedAgents.has("hermes") && agent === "codex" && path === ".agents/skills") : ROOTS.filter(([agent]) => agent !== "sumi");
19758
- const roots = rootDefinitions.map(([agent, path]) => [agent, canonicalAgentPath(join30(home, path), aliases)]);
19885
+ const roots = rootDefinitions.map(([agent, path]) => [agent, canonicalAgentPath(join31(home, path), aliases)]);
19759
19886
  const includesSumi = !selectedAgents || selectedAgents.has("sumi");
19760
19887
  if (includesSumi)
19761
19888
  for (const path of sumiSkillRoots(home))
19762
19889
  roots.push(["sumi", canonicalAgentPath(path, aliases)]);
19763
- const bridgePaths = Object.entries(AGENT_ADAPTERS).filter(([agent]) => agent !== "sumi" || includesSumi).map(([agent]) => canonicalAgentPath(join30(nativeAgentRoot(home, agent), CLI_BRIDGE_NAME), aliases));
19890
+ const bridgePaths = Object.entries(AGENT_ADAPTERS).filter(([agent]) => agent !== "sumi" || includesSumi).map(([agent]) => canonicalAgentPath(join31(nativeAgentRoot(home, agent), CLI_BRIDGE_NAME), aliases));
19764
19891
  for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
19765
19892
  for (const [agent, path] of rootDefinitions)
19766
- roots.push([agent, canonicalAgentPath(join30(project, path), aliases)]);
19893
+ roots.push([agent, canonicalAgentPath(join31(project, path), aliases)]);
19767
19894
  if (selectedAgents?.has("sumi")) {
19768
19895
  for (const path of sumiSkillRoots(home, project))
19769
19896
  roots.push(["sumi", canonicalAgentPath(path, aliases)]);
19770
19897
  for (const directory2 of [".claude", ".agents"])
19771
19898
  for (const name of ["skill", "skills"])
19772
- roots.push(["sumi", canonicalAgentPath(join30(project, directory2, name), aliases)]);
19899
+ roots.push(["sumi", canonicalAgentPath(join31(project, directory2, name), aliases)]);
19773
19900
  }
19774
19901
  }
19775
19902
  const installationInputs = options.codexInstallationInputRoots ?? [];
19776
- if (installationInputs.some((input) => resolve22(input) !== input || realpathSync12(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep13) || input.startsWith(path + sep13)))))
19903
+ if (installationInputs.some((input) => resolve23(input) !== input || realpathSync12(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep14) || input.startsWith(path + sep14)))))
19777
19904
  throw new Error("Native installation input overlaps an unconditional skill-loading root");
19778
19905
  const entries = [], seen = new Set;
19779
19906
  const cacheScans = new Map, maxAliasProofEntries = 1e4;
@@ -19787,19 +19914,19 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
19787
19914
  }
19788
19915
  let reviewedCacheAliasFound = false;
19789
19916
  function verifiedSiblingCacheAlias(agent, path, parent) {
19790
- const link = readlinkSync7(path), lexicalTarget = resolve22(parent, link);
19791
- if (isAbsolute24(link) ? link !== lexicalTarget : dirname21(link) !== ".")
19917
+ const link = readlinkSync7(path), lexicalTarget = resolve23(parent, link);
19918
+ if (isAbsolute24(link) ? link !== lexicalTarget : dirname22(link) !== ".")
19792
19919
  return false;
19793
19920
  const target = canonicalSystemPath(lexicalTarget);
19794
- if (dirname21(target) !== parent)
19921
+ if (dirname22(target) !== parent)
19795
19922
  return false;
19796
- if (!lstatSync27(target, { throwIfNoEntry: false })?.isDirectory())
19923
+ if (!lstatSync28(target, { throwIfNoEntry: false })?.isDirectory())
19797
19924
  return false;
19798
19925
  const scan = cacheScans.get(target);
19799
19926
  if (!scan?.complete || scan.entries > maxAliasProofEntries)
19800
19927
  return false;
19801
19928
  if (scan.hasSkills) {
19802
- const targetSkills = entries.filter((entry) => entry.agent === agent && entry.vendor && (entry.path === target || entry.path.startsWith(target + sep13)));
19929
+ const targetSkills = entries.filter((entry) => entry.agent === agent && entry.vendor && (entry.path === target || entry.path.startsWith(target + sep14)));
19803
19930
  if (path === reviewedCacheAlias && targetSkills.length > 0)
19804
19931
  reviewedCacheAliasFound = true;
19805
19932
  if (agent === "codex" && targetSkills.length > 0 && targetSkills.every((entry) => disabledVendorPaths.has(entry.path) || options.disabledVendorSkill?.(entry) === true))
@@ -19814,11 +19941,11 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
19814
19941
  admit(path);
19815
19942
  const scan = { complete: true, hasSkills: false, entries: 1 };
19816
19943
  assertSafePath(path);
19817
- if (configuredRoot && agent === "codex" && installationInputs.some((root) => path === root || path.startsWith(`${root}${sep13}`)))
19944
+ if (configuredRoot && agent === "codex" && installationInputs.some((root) => path === root || path.startsWith(`${root}${sep14}`)))
19818
19945
  return scan;
19819
19946
  if (!existsSync9(path))
19820
19947
  return scan;
19821
- const stat2 = lstatSync27(path);
19948
+ const stat2 = lstatSync28(path);
19822
19949
  if (stat2.isFile()) {
19823
19950
  if (agent === "sumi" && depth === 1 && path.endsWith(".md"))
19824
19951
  throw new Error("NATIVE_SKILL_DRIFT: flat Sumi skill documents require a dedicated migration adapter");
@@ -19826,21 +19953,21 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
19826
19953
  }
19827
19954
  if (!stat2.isDirectory())
19828
19955
  throw new Error(`Unsupported native discovery entry: ${path}`);
19829
- if (existsSync9(join30(path, "SKILL.md"))) {
19956
+ if (existsSync9(join31(path, "SKILL.md"))) {
19830
19957
  scan.hasSkills = true;
19831
19958
  if (seen.has(path))
19832
19959
  return scan;
19833
19960
  seen.add(path);
19834
19961
  let managed = false;
19835
- const marker = join30(path, ".hasna-skills.json");
19962
+ const marker = join31(path, ".hasna-skills.json");
19836
19963
  if (existsSync9(marker)) {
19837
19964
  try {
19838
- managed = JSON.parse(readFileSync18(marker, "utf8")).managedBy === "@hasna/skills";
19965
+ managed = JSON.parse(readFileSync19(marker, "utf8")).managedBy === "@hasna/skills";
19839
19966
  } catch {}
19840
19967
  }
19841
19968
  const bridge = (!vendor || agent === "sumi") && isOwnedCliBridge(path, bridgePaths);
19842
- const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep13));
19843
- entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" || isCodexSkillDiscoveryInput(path) ? { codexHome: process.env.CODEX_HOME ?? join30(home, ".codex") } : {}, ...agent === "codex" && path.startsWith(canonicalAgentPath(join30(home, ".codex", "skills", ".system"), aliases) + sep13) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve22(home) } : {}, ...rootAlias ? { rootAlias } : {} });
19969
+ const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep14));
19970
+ entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" || isCodexSkillDiscoveryInput(path) ? { codexHome: process.env.CODEX_HOME ?? join31(home, ".codex") } : {}, ...agent === "codex" && path.startsWith(canonicalAgentPath(join31(home, ".codex", "skills", ".system"), aliases) + sep14) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve23(home) } : {}, ...rootAlias ? { rootAlias } : {} });
19844
19971
  return scan;
19845
19972
  }
19846
19973
  if (depth > (agent === "sumi" ? 32 : vendor ? 32 : 3))
@@ -19849,7 +19976,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
19849
19976
  try {
19850
19977
  let child2;
19851
19978
  while ((child2 = directory2.readSync()) !== null) {
19852
- admit(join30(path, child2.name));
19979
+ admit(join31(path, child2.name));
19853
19980
  children.push(child2);
19854
19981
  }
19855
19982
  } finally {
@@ -19859,15 +19986,15 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
19859
19986
  if (pluginCache)
19860
19987
  children.sort((a, b) => Number(a.isSymbolicLink()) - Number(b.isSymbolicLink()));
19861
19988
  for (const { name } of children) {
19862
- if (name === "node_modules")
19989
+ if (name === "node_modules" && !(agent === "codex" && (!pluginCache || codexPluginCacheSkillTree(join31(path, name), depth + 1))))
19863
19990
  continue;
19864
- if (name.startsWith(".") && name !== ".system" && agent !== "hermes" && agent !== "sumi" && !(options.guardHermes && `${path}${sep13}`.includes(`${sep13}.agents${sep13}skills${sep13}`)))
19991
+ if (name.startsWith(".") && name !== ".system" && agent !== "hermes" && agent !== "sumi" && !(options.guardHermes && `${path}${sep14}`.includes(`${sep14}.agents${sep14}skills${sep14}`)))
19865
19992
  continue;
19866
19993
  const isVendor = vendor || name === ".system";
19867
19994
  if (isVendor && !options.includeVendor)
19868
19995
  continue;
19869
- const child2 = join30(path, name);
19870
- if (isVendor && lstatSync27(child2, { throwIfNoEntry: false })?.isSymbolicLink()) {
19996
+ const child2 = join31(path, name);
19997
+ if (isVendor && lstatSync28(child2, { throwIfNoEntry: false })?.isSymbolicLink()) {
19871
19998
  const target = statSync11(child2, { throwIfNoEntry: false });
19872
19999
  if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child2, path)) {
19873
20000
  scan.entries++;
@@ -19884,7 +20011,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
19884
20011
  return scan;
19885
20012
  }
19886
20013
  function scanRoot(agent, path, vendor = false, pluginCache = false, configuredRoot = false) {
19887
- if (agent === "hermes" || options.guardHermes && `${path}${sep13}`.includes(`${sep13}.agents${sep13}skills${sep13}`))
20014
+ if (agent === "hermes" || options.guardHermes && `${path}${sep14}`.includes(`${sep14}.agents${sep14}skills${sep14}`))
19888
20015
  assertNoHermesLegacyShadow(path);
19889
20016
  if (!visit2(agent, path, vendor, 0, pluginCache, false, configuredRoot).complete)
19890
20017
  throw new Error(`Native skill discovery limit exceeded; review this root before continuing: ${path}`);
@@ -19893,11 +20020,11 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
19893
20020
  scanRoot(agent, path);
19894
20021
  if (options.includeVendor) {
19895
20022
  for (const agent of ["codex", "claude"].filter(includesAgent))
19896
- scanRoot(agent, canonicalAgentPath(join30(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
20023
+ scanRoot(agent, canonicalAgentPath(join31(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
19897
20024
  if (includesAgent("claude"))
19898
- scanRoot("claude", canonicalAgentPath(join30(home, ".claude", "plugins", "synced"), aliases), true);
20025
+ scanRoot("claude", canonicalAgentPath(join31(home, ".claude", "plugins", "synced"), aliases), true);
19899
20026
  if (includesAgent("gemini"))
19900
- scanRoot("gemini", join30(home, ".gemini", "extensions"), true);
20027
+ scanRoot("gemini", join31(home, ".gemini", "extensions"), true);
19901
20028
  }
19902
20029
  const configured = options.configured ? INTEGRATION_AGENTS.filter(includesAgent).flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
19903
20030
  for (const root of [...options.agentRoots ?? [], ...configured])
@@ -19910,7 +20037,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
19910
20037
  }
19911
20038
  function readOptional(path) {
19912
20039
  assertSafePath(path);
19913
- return existsSync9(path) ? readFileSync18(path, "utf8") : null;
20040
+ return existsSync9(path) ? readFileSync19(path, "utf8") : null;
19914
20041
  }
19915
20042
  function jsonObject(text2, path) {
19916
20043
  if (text2 === null)
@@ -20010,19 +20137,19 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
20010
20137
  function disableCodexSkills(text2, skills, aliases, bridgePath) {
20011
20138
  const bundled = disableCodexBundledSkills(text2);
20012
20139
  let result = normalizeCodexInlinePathConfig(bundled);
20013
- const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve22(bridgePath), resolve22(join30(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
20140
+ const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve23(bridgePath), resolve23(join31(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
20014
20141
  const previousConfig = Bun.TOML.parse(bundled).skills?.config;
20015
20142
  if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
20016
20143
  throw new Error("Codex skill controls require one path or name selector per [[skills.config]] entry; review ambiguous entries before running skills hook install");
20017
20144
  const bridgeNeedsRepair = previousConfig?.some((entry) => entry.enabled === false && bridgeSelector(entry));
20018
20145
  const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
20019
20146
  for (const skill of selections) {
20020
- const path = join30(skill.path, "SKILL.md");
20147
+ const path = join31(skill.path, "SKILL.md");
20021
20148
  let found = false;
20022
20149
  result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
20023
20150
  const parsed = Bun.TOML.parse(section);
20024
20151
  const declared = parsed.skills?.config?.[0];
20025
- if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve22(path), resolve22(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
20152
+ if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve23(path), resolve23(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
20026
20153
  return section;
20027
20154
  found = true;
20028
20155
  return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
@@ -20043,8 +20170,8 @@ enabled = false
20043
20170
  return result;
20044
20171
  }
20045
20172
  function assertCodexInstallationInputs(home, projectDirs, inputs) {
20046
- const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory2) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve22(directory2, path)));
20047
- if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep13) || input.startsWith(root + sep13))))
20173
+ const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory2) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve23(directory2, path)));
20174
+ if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep14) || input.startsWith(root + sep14))))
20048
20175
  throw new Error("Native installation input overlaps an unconditional skill-loading root");
20049
20176
  }
20050
20177
  function planAgentIntegration(options) {
@@ -20054,7 +20181,7 @@ function planAgentIntegration(options) {
20054
20181
  throw new Error("Explicit Codex skill denials require a fresh native catalog and discovery review");
20055
20182
  const home = options.home ?? homedir2(), dataDir2 = options.dataDir ?? getDataDirReadOnly();
20056
20183
  const aliases = rootAliases(home, options.allowRootAliases);
20057
- const policyPath = join30(dataDir2, "agent-policy.json");
20184
+ const policyPath = join31(dataDir2, "agent-policy.json");
20058
20185
  assertSafePath(policyPath);
20059
20186
  const priorSnapshot = readManagedSkillPolicySnapshot(dataDir2), previousPolicy = priorSnapshot?.text ?? null, policy = priorSnapshot?.value ?? {};
20060
20187
  if (policy.bridge !== undefined && (!policy.bridge || typeof policy.bridge !== "object" || Array.isArray(policy.bridge)))
@@ -20085,34 +20212,42 @@ function planAgentIntegration(options) {
20085
20212
  const executable2 = discoveryExecutable(agent) ?? (prior ? recordedDiscoveryExecutable(agent, prior) : null);
20086
20213
  executables.set(agent, executable2);
20087
20214
  const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? { ...policy.bridge.discovery[agent], ...agent === "codex" ? { codexDisabledPluginSkills: policy.bridge.codexPluginSkills ?? [] } : {} } : undefined;
20088
- if (retainedReview) {
20089
- assertManagedAgentBridge(agent, { home, dataDir: dataDir2, projectDir: options.projectDir ?? home });
20090
- retainedAgents.push(agent);
20215
+ try {
20216
+ if (retainedReview) {
20217
+ assertRetainedReviewState(agent, { home, dataDir: dataDir2, projectDir: options.projectDir ?? home });
20218
+ retainedAgents.push(agent);
20219
+ }
20220
+ return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases), executable: executable2 });
20221
+ } catch (error) {
20222
+ if (isDiscoveryRootUnresolved(error) || error instanceof SumiPathResolverError || error instanceof NativeSkillCopiesDriftError)
20223
+ throw error;
20224
+ if (!retainedReview)
20225
+ throw error;
20226
+ throw new Error(`NATIVE_SKILL_DRIFT: ${error.message.replace(/^NATIVE_SKILL_DRIFT: /, "")}. This agent reuses a saved discovery review and that review no longer verifies, so a fresh explicit review is required: skills hook install --agent ${agent} --discovery-inputs <review> --apply.`);
20091
20227
  }
20092
- return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases), executable: executable2 });
20093
20228
  });
20094
- const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join30(home, ".codex", "config.toml"), aliases)) : null;
20229
+ const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join31(home, ".codex", "config.toml"), aliases)) : null;
20095
20230
  const codexConfig = Bun.TOML.parse(codexConfigBefore ?? "");
20096
20231
  const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, disabledVendorPaths: disabledCodexSkillPaths(codexConfig, aliases) });
20097
- const codexPathConfig = options.agents.includes("codex") ? disableCodexSkills(codexConfigBefore ?? "", nativeSkills, aliases, canonicalAgentPath(join30(home, ".codex/skills", CLI_BRIDGE_NAME), aliases)) : undefined;
20232
+ const codexPathConfig = options.agents.includes("codex") ? disableCodexSkills(codexConfigBefore ?? "", nativeSkills, aliases, canonicalAgentPath(join31(home, ".codex/skills", CLI_BRIDGE_NAME), aliases)) : undefined;
20098
20233
  const plannedCodexRules = codexPathConfig === undefined ? undefined : Bun.TOML.parse(codexPathConfig).skills?.config ?? [];
20099
20234
  const observedNativeSources = [];
20100
- const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join30(entry.path, "SKILL.md")), canonicalAgentPath(join30(home, ".codex/plugins/cache"), aliases), resolve22(options.projectDir ?? home), (path) => {
20235
+ const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join31(entry.path, "SKILL.md")), canonicalAgentPath(join31(home, ".codex/plugins/cache"), aliases), resolve23(options.projectDir ?? home), (path) => {
20101
20236
  const bytes = readNativeBytes(path, 1024 * 1024);
20102
20237
  observedNativeSources.push({ path, sha256: sha4(bytes) });
20103
20238
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
20104
20239
  }, codexConfig.skills?.config ?? [], codexConfig.plugins, plannedCodexRules, options.codexSkillDenials) : { skills: policy.bridge?.codexPluginSkills ?? [], inactivePlugins: policy.bridge?.codexInactivePlugins ?? [], sourceInputs: policy.bridge?.discovery?.codex?.codexInstallationInputs?.plugins ?? [] };
20105
- const cache = canonicalAgentPath(join30(home, ".codex/plugins/cache"), aliases);
20240
+ const cache = canonicalAgentPath(join31(home, ".codex/plugins/cache"), aliases);
20106
20241
  const retired = (policy.bridge?.codexPluginSkills ?? []).filter((control) => absentDisabledCodexPluginParent(cache, control.pluginParent, policy.bridge.codexPluginSkills, codexConfig.skills?.config ?? []));
20107
20242
  for (const denial of options.codexSkillDenials ?? []) {
20108
20243
  const control = codexPluginControls.skills.find((item) => item.name === denial.name);
20109
20244
  if (!control)
20110
20245
  throw new Error("Explicit Codex denial did not resolve a reviewed identity");
20111
- const root = join30(control.pluginParent, relative9(control.pluginParent, denial.path).split(sep13)[0]);
20246
+ const root = join31(control.pluginParent, relative9(control.pluginParent, denial.path).split(sep14)[0]);
20112
20247
  const review = options.discoveryInputs.agents.find((item) => item.agent === "codex");
20113
- for (const path of [join30(root, ".app.json"), join30(root, ".mcp.json"), join30(root, "hooks")])
20248
+ for (const path of [join31(root, ".app.json"), join31(root, ".mcp.json"), join31(root, "hooks")])
20114
20249
  observedNativeSources.push({ path, sha256: nativeSourceDigest(path) });
20115
- const files = [join30(root, ".codex-plugin/plugin.json"), ...control.appSha256 ? [join30(root, ".app.json")] : [], ...control.mcpSha256 ? [join30(root, ".mcp.json")] : []];
20250
+ const files = [join31(root, ".codex-plugin/plugin.json"), ...control.appSha256 ? [join31(root, ".app.json")] : [], ...control.mcpSha256 ? [join31(root, ".mcp.json")] : []];
20116
20251
  for (const path of files)
20117
20252
  if (!review?.sources.some((source) => source.path === path && source.format === undefined && source.fields === undefined && [undefined, "bytes"].includes(source.hashMode) && source.sha256 === sha4(readNativeBytes(path, 1024 * 1024))))
20118
20253
  throw new Error("Explicit Codex denial requires current full manifest and capability discovery witnesses");
@@ -20121,12 +20256,12 @@ function planAgentIntegration(options) {
20121
20256
  for (const discovery of discoveries)
20122
20257
  if (discovery.agent === "codex" && discovery.method === "reviewed")
20123
20258
  discovery.codexDisabledPluginSkills = codexPluginSkills;
20124
- if (options.agents.includes("codex") && !options.codexNativeCatalog && codexPluginSkills.length && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join30(home, ".codex/plugins/cache"), aliases), codexPluginSkills, (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), codexConfig.skills?.config ?? [])) {
20259
+ if (options.agents.includes("codex") && !options.codexNativeCatalog && codexPluginSkills.length && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join31(home, ".codex/plugins/cache"), aliases), codexPluginSkills, (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), codexConfig.skills?.config ?? [])) {
20125
20260
  throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; provide a fresh native catalog and discovery review");
20126
20261
  }
20127
20262
  const codexInactivePlugins = codexPluginControls.inactivePlugins;
20128
20263
  const codexDiscovery = discoveries.find((binding) => binding.agent === "codex");
20129
- const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot + sep13) || input.sourceRoot.startsWith(root + sep13)) && codexDiscovery.sources.some((source) => source.path === join30(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
20264
+ const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot + sep14) || input.sourceRoot.startsWith(root + sep14)) && codexDiscovery.sources.some((source) => source.path === join31(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
20130
20265
  assertCodexInstallationInputs(home, [options.projectDir ?? home], codexPluginSourceInputs.map((input) => input.sourceRoot));
20131
20266
  const changes = [];
20132
20267
  let observedSettings;
@@ -20134,19 +20269,19 @@ function planAgentIntegration(options) {
20134
20269
  if (!INTEGRATION_AGENTS.includes(agent))
20135
20270
  throw new Error(`Unsupported agent: ${agent}`);
20136
20271
  const { command, profileId: profileId2 } = bindings.get(agent);
20137
- const bridgePath = canonicalAgentPath(join30(nativeAgentRoot(home, agent, options.projectDir ?? process.cwd()), CLI_BRIDGE_NAME), aliases);
20272
+ const bridgePath = canonicalAgentPath(join31(nativeAgentRoot(home, agent, options.projectDir ?? process.cwd()), CLI_BRIDGE_NAME), aliases);
20138
20273
  assertSafePath(bridgePath);
20139
20274
  if (existsSync9(bridgePath) && !isOwnedCliBridge(bridgePath, [bridgePath]))
20140
20275
  throw new Error(`Refusing to overwrite an unrecognized or modified Skills bridge: ${bridgePath}`);
20141
20276
  for (const [name, after2] of Object.entries(CLI_BRIDGE_FILES)) {
20142
- const path2 = join30(bridgePath, name), before2 = readOptional(path2);
20277
+ const path2 = join31(bridgePath, name), before2 = readOptional(path2);
20143
20278
  if (before2 !== after2)
20144
20279
  changes.push({ path: path2, before: before2, after: after2 });
20145
20280
  }
20146
20281
  const path = canonicalAgentPath(nativeAgentConfig(home, agent, options.projectDir ?? process.cwd()), aliases);
20147
20282
  const before = readOptional(path);
20148
20283
  if (agent === "hermes") {
20149
- const supervisorPath = join30(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(command, profileId2);
20284
+ const supervisorPath = join31(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(command, profileId2);
20150
20285
  const priorCommand = policy.bridge?.commands?.hermes, priorProfile = policy.bridge?.profiles?.hermes ?? policy.profileId;
20151
20286
  const previous = policy.bridge?.supervisors?.hermes;
20152
20287
  if (supervisorBefore !== null && supervisorBefore !== supervisorAfter && !(previous?.path === supervisorPath && typeof priorCommand === "string" && typeof priorProfile === "string" && supervisorBefore === renderHermesSupervisor(priorCommand, priorProfile)))
@@ -20157,7 +20292,7 @@ function planAgentIntegration(options) {
20157
20292
  const after2 = configureHermesHooks(before, supervisor, previous);
20158
20293
  if (before !== after2)
20159
20294
  changes.push({ path, before, after: after2 });
20160
- const optOut = join30(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
20295
+ const optOut = join31(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
20161
20296
  if (optOutBefore === null)
20162
20297
  changes.push({ path: optOut, before: null, after: `Managed by @hasna/skills: bundled native skill reseeding is disabled.
20163
20298
  ` });
@@ -20168,7 +20303,7 @@ function planAgentIntegration(options) {
20168
20303
  if (config.permissions !== undefined && (!Array.isArray(config.permissions) || config.permissions.some((rule) => !rule || typeof rule.action !== "string" || typeof rule.resource !== "string" || !["allow", "deny", "ask"].includes(rule.effect))))
20169
20304
  throw new Error("Expected Sumi native permission rules");
20170
20305
  config.permissions = [...(config.permissions ?? []).filter((rule) => rule.action !== "skill"), ...SUMI_SKILL_PERMISSIONS];
20171
- const pluginPath = join30(sumiConfigDirectory(home, process.env, options.projectDir ?? process.cwd()), "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
20306
+ const pluginPath = join31(sumiConfigDirectory(home, process.env, options.projectDir ?? process.cwd()), "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
20172
20307
  const pluginAfter = renderSumiPlugin(command, profileId2);
20173
20308
  const priorCommand = policy.bridge?.commands?.sumi, priorProfile = policy.bridge?.profiles?.sumi ?? policy.profileId;
20174
20309
  if (pluginBefore !== null && pluginBefore !== pluginAfter && !(typeof priorCommand === "string" && typeof priorProfile === "string" && isManagedSumiPlugin(pluginBefore, priorCommand, priorProfile)))
@@ -20180,7 +20315,7 @@ function planAgentIntegration(options) {
20180
20315
  if (!permission || typeof permission !== "object" || Array.isArray(permission))
20181
20316
  throw new Error("Expected OpenCode permission object");
20182
20317
  config.permission = { ...permission, skill: { "*": "deny", [CLI_BRIDGE_NAME]: "allow" } };
20183
- const pluginPath = join30(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
20318
+ const pluginPath = join31(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
20184
20319
  const pluginAfter = renderOpenCodePlugin(command, profileId2);
20185
20320
  const priorCommand = policy.bridge?.commands?.opencode;
20186
20321
  const ownedPrevious = typeof priorCommand === "string" && typeof policy.profileId === "string" && pluginBefore === renderOpenCodePlugin(priorCommand, policy.bridge?.profiles?.opencode ?? policy.profileId);
@@ -20197,7 +20332,7 @@ function planAgentIntegration(options) {
20197
20332
  if (before !== after)
20198
20333
  changes.push({ path, before, after });
20199
20334
  if (agent === "codex") {
20200
- const configPath = canonicalAgentPath(join30(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
20335
+ const configPath = canonicalAgentPath(join31(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
20201
20336
  if (previous !== codexConfigBefore)
20202
20337
  throw new Error(`Configuration changed during planning: ${configPath}`);
20203
20338
  const next = disableReviewedCodexPluginNames(codexPathConfig, codexPluginSkills);
@@ -20209,28 +20344,28 @@ function planAgentIntegration(options) {
20209
20344
  }
20210
20345
  }
20211
20346
  }
20212
- const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha4(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join30(home, ".codex/config.toml"), aliases))?.before === null ? null : sha4(readOptional(canonicalAgentPath(join30(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview;
20347
+ const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha4(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join31(home, ".codex/config.toml"), aliases))?.before === null ? null : sha4(readOptional(canonicalAgentPath(join31(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview;
20213
20348
  const discoveryAfter = discoveries.map((binding) => {
20214
20349
  const rebound = rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after])));
20215
20350
  if (binding.agent !== "codex")
20216
20351
  return rebound;
20217
20352
  const { codexInstallationInputs: previousInputs, ...current } = rebound;
20218
20353
  const inputs = codexPluginSourceInputs.map((input) => input.sourceRoot);
20219
- const directories = options.codexNativeCatalog || !previousInputs ? captureDiscoveryDirectories((binding.directories ?? []).filter((directory2) => inputs.some((root) => root.startsWith(directory2.path + sep13))).map((directory2) => directory2.path), inputs) : previousInputs.directories;
20354
+ const directories = options.codexNativeCatalog || !previousInputs ? captureDiscoveryDirectories((binding.directories ?? []).filter((directory2) => inputs.some((root) => root.startsWith(directory2.path + sep14))).map((directory2) => directory2.path), inputs) : previousInputs.directories;
20220
20355
  const installed = codexPluginSourceInputs.length ? { ...current, codexInstallationInputs: { version: codexPluginSkillReview.version, catalogSha256: codexPluginSkillReview.catalogSha256, plugins: codexPluginSourceInputs, ...directories?.length ? { directories } : {} } } : current;
20221
- return captureRetiredCodexDiscovery(installed, codexPluginSkills, Bun.TOML.parse(changes.find((change) => change.path === canonicalAgentPath(join30(home, ".codex/config.toml"), aliases))?.after ?? codexConfigBefore ?? "").skills?.config ?? []);
20356
+ return captureRetiredCodexDiscovery(installed, codexPluginSkills, Bun.TOML.parse(changes.find((change) => change.path === canonicalAgentPath(join31(home, ".codex/config.toml"), aliases))?.after ?? codexConfigBefore ?? "").skills?.config ?? []);
20222
20357
  });
20223
20358
  const nextPolicy = { ...policy, version: 1, loading: "cli", profileId, bridge: {
20224
20359
  ...policy.bridge,
20225
20360
  version: CLI_BRIDGE_VERSION,
20226
20361
  digest: CLI_BRIDGE_DIGEST,
20227
20362
  agents: [...new Set([...priorAgents, ...options.agents])].sort(),
20228
- home: resolve22(home),
20363
+ home: resolve23(home),
20229
20364
  includeVendor: true,
20230
- ...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join30(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha4(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
20365
+ ...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join31(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha4(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
20231
20366
  commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
20232
20367
  profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
20233
- disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join30(home, ".codex", "skills", ".system"), aliases) + sep13)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
20368
+ disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join31(home, ".codex", "skills", ".system"), aliases) + sep14)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
20234
20369
  rootAliases: aliases,
20235
20370
  ...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
20236
20371
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
@@ -20273,7 +20408,7 @@ function projectedHookAcl(path, observed) {
20273
20408
  const identity5 = observed.isDirectory() ? (stat2) => `${stat2.dev}:${stat2.ino}:${stat2.uid}:${stat2.mode}` : (stat2) => `${stat2.dev}:${stat2.ino}:${stat2.ctimeNs}:${stat2.uid}:${stat2.mode}:${stat2.nlink}:${stat2.size}:${stat2.mtimeNs}`;
20274
20409
  const fd = openSync17(path, constants18.O_RDONLY | constants18.O_NOFOLLOW | constants18.O_NONBLOCK | (observed.isDirectory() ? constants18.O_DIRECTORY : 0));
20275
20410
  try {
20276
- if (identity5(fstatSync17(fd, { bigint: true })) !== identity5(observed) || !hasTrustedDarwinAcl(fd, true) || identity5(fstatSync17(fd, { bigint: true })) !== identity5(observed) || identity5(lstatSync27(path, { bigint: true })) !== identity5(observed))
20411
+ if (identity5(fstatSync17(fd, { bigint: true })) !== identity5(observed) || !hasTrustedDarwinAcl(fd, true) || identity5(fstatSync17(fd, { bigint: true })) !== identity5(observed) || identity5(lstatSync28(path, { bigint: true })) !== identity5(observed))
20277
20412
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
20278
20413
  } finally {
20279
20414
  closeSync17(fd);
@@ -20281,23 +20416,23 @@ function projectedHookAcl(path, observed) {
20281
20416
  }
20282
20417
  function projectedHookExecutable(command, expectedResolved) {
20283
20418
  try {
20284
- if (!isAbsolute24(command) || /[\0\r\n]/.test(command) || resolve22(command) !== command)
20419
+ if (!isAbsolute24(command) || /[\0\r\n]/.test(command) || resolve23(command) !== command)
20285
20420
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
20286
20421
  command = canonicalSystemPath(command);
20287
20422
  const parents2 = (file = command) => {
20288
20423
  const result = [];
20289
- for (let path = dirname21(file);; path = dirname21(path)) {
20290
- const stat2 = lstatSync27(path, { bigint: true });
20424
+ for (let path = dirname22(file);; path = dirname22(path)) {
20425
+ const stat2 = lstatSync28(path, { bigint: true });
20291
20426
  if (!stat2.isDirectory() || !trustedAncestorOwner(stat2.uid) || (stat2.mode & 0o022n) !== 0n && !(stat2.uid === 0n && (stat2.mode & 0o1000n) !== 0n))
20292
20427
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
20293
20428
  projectedHookAcl(path, stat2);
20294
20429
  result.push(`${path}:${stat2.dev}:${stat2.ino}:${stat2.uid}:${stat2.mode}`);
20295
- if (dirname21(path) === path)
20430
+ if (dirname22(path) === path)
20296
20431
  break;
20297
20432
  }
20298
20433
  return result;
20299
20434
  };
20300
- const parentIdentity = parents2(), link = lstatSync27(command, { bigint: true });
20435
+ const parentIdentity = parents2(), link = lstatSync28(command, { bigint: true });
20301
20436
  if (link.uid !== BigInt(process.getuid()) || !link.isFile() && !link.isSymbolicLink())
20302
20437
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
20303
20438
  const linkIdentity = (stat2) => `${stat2.dev}:${stat2.ino}:${stat2.mode}:${stat2.uid}:${stat2.ctimeNs}`;
@@ -20317,18 +20452,18 @@ function projectedHookExecutable(command, expectedResolved) {
20317
20452
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
20318
20453
  const path = target.pinned.runtime;
20319
20454
  assertPinnedLauncherRuntimeSafe(path);
20320
- if (resolve22(path) !== path)
20455
+ if (resolve23(path) !== path)
20321
20456
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
20322
- runtime = { path, parents: parents2(path), stat: lstatSync27(path, { bigint: true }) };
20457
+ runtime = { path, parents: parents2(path), stat: lstatSync28(path, { bigint: true }) };
20323
20458
  if (!runtime.stat.isFile())
20324
20459
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
20325
20460
  projectedHookAcl(path, runtime.stat);
20326
20461
  }
20327
20462
  const resolvedParentIdentity = parents2(resolved);
20328
- const manifest = snapshot(join30(dirname21(dirname21(resolved)), "package.json"), false, { readOnlyPackage: true });
20463
+ const manifest = snapshot(join31(dirname22(dirname22(resolved)), "package.json"), false, { readOnlyPackage: true });
20329
20464
  projectedHookAcl(manifest.file, manifest.stat);
20330
20465
  const pkg = JSON.parse(manifest.text);
20331
- if (pkg.name !== "@hasna/skills" || !/^\d+\.\d+\.\d+$/.test(pkg.version) || pkg.bin?.skills !== "bin/index.js" || resolved !== join30(dirname21(manifest.file), "bin/index.js"))
20466
+ if (pkg.name !== "@hasna/skills" || !/^\d+\.\d+\.\d+$/.test(pkg.version) || pkg.bin?.skills !== "bin/index.js" || resolved !== join31(dirname22(manifest.file), "bin/index.js"))
20332
20467
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
20333
20468
  const cli = snapshot(resolved, false, { readOnlyPackage: true });
20334
20469
  projectedHookAcl(cli.file, cli.stat);
@@ -20337,7 +20472,7 @@ function projectedHookExecutable(command, expectedResolved) {
20337
20472
  const recheck = () => {
20338
20473
  try {
20339
20474
  const again = resolveLauncherCommand(command);
20340
- if (again.entry !== resolved || again.physical !== target.physical || linkIdentity(lstatSync27(command, { bigint: true })) !== linkIdentity(link) || !direct() || !isDeepStrictEqual8(parents2(), parentIdentity) || !isDeepStrictEqual8(parents2(resolved), resolvedParentIdentity))
20475
+ if (again.entry !== resolved || again.physical !== target.physical || linkIdentity(lstatSync28(command, { bigint: true })) !== linkIdentity(link) || !direct() || !isDeepStrictEqual8(parents2(), parentIdentity) || !isDeepStrictEqual8(parents2(resolved), resolvedParentIdentity))
20341
20476
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
20342
20477
  if (launcher) {
20343
20478
  if (!isDeepStrictEqual8(parents2(target.physical), launcherParentIdentity))
@@ -20377,8 +20512,8 @@ function planClaudeManagedHookProjection(options) {
20377
20512
  } catch {
20378
20513
  hookProjectionRefusal("TARGET_INVALID");
20379
20514
  }
20380
- const home = resolve22(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
20381
- const policyPath = join30(dataDir2, "agent-policy.json");
20515
+ const home = resolve23(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
20516
+ const policyPath = join31(dataDir2, "agent-policy.json");
20382
20517
  let policyBytes, settingsBytes, settingsPath, command, profile;
20383
20518
  try {
20384
20519
  policyBytes = readNativeBytes(policyPath, 1024 * 1024);
@@ -20386,7 +20521,7 @@ function planClaudeManagedHookProjection(options) {
20386
20521
  if (!snapshot2 || snapshot2.text !== policyBytes.toString("utf8"))
20387
20522
  hookProjectionRefusal("SOURCE_UNVERIFIED");
20388
20523
  const binding = snapshot2.value.bridge;
20389
- settingsPath = canonicalAgentPath(join30(home, AGENT_ADAPTERS.claude.config), binding?.rootAliases ?? []);
20524
+ settingsPath = canonicalAgentPath(join31(home, AGENT_ADAPTERS.claude.config), binding?.rootAliases ?? []);
20390
20525
  settingsBytes = readNativeBytes(settingsPath, 1024 * 1024);
20391
20526
  command = binding?.commands?.claude;
20392
20527
  profile = binding?.profiles?.claude;
@@ -20472,7 +20607,7 @@ function claudeSettingsConsumers(home, path, discoveries, aliases) {
20472
20607
  const canonical9 = (value) => canonicalAgentPath(value, aliases);
20473
20608
  const containsSettings = (root) => {
20474
20609
  const distance = relative9(canonical9(root), path);
20475
- return distance === "" || distance !== ".." && !distance.startsWith(`..${sep13}`) && !isAbsolute24(distance);
20610
+ return distance === "" || distance !== ".." && !distance.startsWith(`..${sep14}`) && !isAbsolute24(distance);
20476
20611
  };
20477
20612
  return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || binding.agent !== "sumi" && canonical9(nativeAgentConfig(home, binding.agent)) === path || binding.sources.some((source) => canonical9(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory2) => containsSettings(directory2.path))).map((binding) => binding.agent);
20478
20613
  }
@@ -20490,8 +20625,8 @@ function verifyCoordinatedDiscovery(binding, home, aliases, executable2) {
20490
20625
  }
20491
20626
  }
20492
20627
  function planClaudeHookEventsUpdate(options) {
20493
- const home = resolve22(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
20494
- const policyPath = join30(dataDir2, "agent-policy.json");
20628
+ const home = resolve23(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
20629
+ const policyPath = join31(dataDir2, "agent-policy.json");
20495
20630
  assertSafePath(policyPath);
20496
20631
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
20497
20632
  if (!snapshot2)
@@ -20502,9 +20637,9 @@ function planClaudeHookEventsUpdate(options) {
20502
20637
  }
20503
20638
  const aliases = bridge.rootAliases ?? [];
20504
20639
  recheckRootAliases(aliases);
20505
- const path = canonicalAgentPath(join30(home, ".claude", "settings.json"), aliases);
20640
+ const path = canonicalAgentPath(join31(home, ".claude", "settings.json"), aliases);
20506
20641
  assertSafePath(path);
20507
- const before = lstatSync27(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
20642
+ const before = lstatSync28(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
20508
20643
  if ((before === null ? "absent" : sha4(before)) !== options.expectedSettingsSha256)
20509
20644
  throw new Error("Claude settings preimage changed");
20510
20645
  assertClaudeHookEventsReplacement(before, options.replacement, options.events);
@@ -20560,7 +20695,7 @@ function planClaudeHookEventsUpdate(options) {
20560
20695
  }
20561
20696
  function atomicWrite2(path, content) {
20562
20697
  assertSafePath(path);
20563
- mkdirSync8(dirname21(path), { recursive: true, mode: 448 });
20698
+ mkdirSync8(dirname22(path), { recursive: true, mode: 448 });
20564
20699
  const temporary = `${path}.skills-${randomUUID4()}`;
20565
20700
  try {
20566
20701
  writeFileSync6(temporary, content, { mode: 384, flag: "wx" });
@@ -20575,7 +20710,7 @@ function planAgentSettingsWitnessUpgrade(options) {
20575
20710
  throw new Error("Invalid Codex witness target version");
20576
20711
  if (options.targetClaudeVersion !== undefined && (options.agent !== "claude" || ![3, 4].includes(options.targetClaudeVersion)))
20577
20712
  throw new Error("Invalid Claude witness target version");
20578
- const home = resolve22(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
20713
+ const home = resolve23(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
20579
20714
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
20580
20715
  if (!snapshot2 || sha4(snapshot2.text) !== options.expectedPolicySha256)
20581
20716
  throw new Error("Managed policy preimage changed");
@@ -20584,7 +20719,7 @@ function planAgentSettingsWitnessUpgrade(options) {
20584
20719
  const binding = policy.bridge?.discovery?.[options.agent];
20585
20720
  if (!binding || binding.agent !== options.agent || binding.method !== "reviewed")
20586
20721
  throw new Error("Settings witness upgrade requires an existing reviewed native binding");
20587
- const configPath = canonicalAgentPath(options.agent === "sumi" ? nativeAgentConfig(home, "sumi", options.projectDir ?? process.cwd()) : join30(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
20722
+ const configPath = canonicalAgentPath(options.agent === "sumi" ? nativeAgentConfig(home, "sumi", options.projectDir ?? process.cwd()) : join31(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
20588
20723
  const settings = readOptional(configPath);
20589
20724
  if (settings === null || sha4(settings) !== options.expectedSettingsSha256)
20590
20725
  throw new Error("Native settings preimage changed");
@@ -20635,12 +20770,12 @@ function planAgentSettingsWitnessUpgrade(options) {
20635
20770
  const replacement = { ...binding, sources: rebound };
20636
20771
  verifyAgentDiscovery(replacement, undefined, aliases);
20637
20772
  const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
20638
- return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join30(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join30(dataDir2, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], ...policy.bridge.discoveryExecutables ? { discoveryExecutables: policy.bridge.discoveryExecutables } : {}, rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
20773
+ return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join31(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join31(dataDir2, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], ...policy.bridge.discoveryExecutables ? { discoveryExecutables: policy.bridge.discoveryExecutables } : {}, rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
20639
20774
  }
20640
20775
  function planCodexSemanticCacheWitnessUpgrade(options) {
20641
20776
  if (options.agent !== "codex" || !/^[a-f0-9]{64}$/.test(options.expectedPolicySha256) || !/^[a-f0-9]{64}$/.test(options.expectedSettingsSha256) || options.expectedCacheWitnessSha256 !== undefined && !/^[a-f0-9]{64}$/.test(options.expectedCacheWitnessSha256) || !Array.isArray(options.appOnlyParents) || options.appOnlyParents.length > 4096 || new Set(options.appOnlyParents).size !== options.appOnlyParents.length || options.reviewedHookParents !== undefined && (!Array.isArray(options.reviewedHookParents) || options.reviewedHookParents.length > 4096 || new Set(options.reviewedHookParents).size !== options.reviewedHookParents.length))
20642
20777
  throw new Error("Invalid Codex semantic cache upgrade request");
20643
- const home = resolve22(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
20778
+ const home = resolve23(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
20644
20779
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
20645
20780
  if (!snapshot2 || sha4(snapshot2.text) !== options.expectedPolicySha256)
20646
20781
  throw new Error("Managed policy preimage changed");
@@ -20654,10 +20789,10 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
20654
20789
  throw new Error("Cache upgrade requires prior installed-plugin review");
20655
20790
  if (!controls.length || !isDeepStrictEqual8(controls, binding.codexDisabledPluginSkills ?? controls))
20656
20791
  throw new Error("Cache upgrade requires unchanged enrolled plugin controls");
20657
- const cacheRoot = canonicalAgentPath(join30(home, ".codex/plugins/cache"), aliases);
20658
- if (controls.some((control) => dirname21(dirname21(control.pluginParent)) !== cacheRoot))
20792
+ const cacheRoot = canonicalAgentPath(join31(home, ".codex/plugins/cache"), aliases);
20793
+ if (controls.some((control) => dirname22(dirname22(control.pluginParent)) !== cacheRoot))
20659
20794
  throw new Error("Cache upgrade names another enrolled cache");
20660
- const configPath = canonicalAgentPath(join30(home, ".codex/config.toml"), aliases), settings = readOptional(configPath);
20795
+ const configPath = canonicalAgentPath(join31(home, ".codex/config.toml"), aliases), settings = readOptional(configPath);
20661
20796
  if (settings === null || sha4(settings) !== options.expectedSettingsSha256)
20662
20797
  throw new Error("Native settings preimage changed");
20663
20798
  const boundedRead = (path) => new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024));
@@ -20680,13 +20815,13 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
20680
20815
  return unchangedFullSource(path);
20681
20816
  };
20682
20817
  const appOnlyParents = options.appOnlyParents.map((parent) => {
20683
- const parts = relative9(cacheRoot, parent).split(sep13);
20684
- if (!isAbsolute24(parent) || resolve22(parent) !== parent || parts.length !== 2 || parts.some((part) => !/^[A-Za-z0-9_-]{1,64}$/.test(part)) || controls.some((control) => control.pluginParent === parent))
20818
+ const parts = relative9(cacheRoot, parent).split(sep14);
20819
+ if (!isAbsolute24(parent) || resolve23(parent) !== parent || parts.length !== 2 || parts.some((part) => !/^[A-Za-z0-9_-]{1,64}$/.test(part)) || controls.some((control) => control.pluginParent === parent))
20685
20820
  throw new Error("Unsupported app-only parent role");
20686
- const receipt = JSON.parse(unchangedFullSource(join30(parent, ".codex-remote-plugin-install.json")));
20821
+ const receipt = JSON.parse(unchangedFullSource(join31(parent, ".codex-remote-plugin-install.json")));
20687
20822
  if (!receipt || Object.keys(receipt).length !== 2 || receipt.schema_version !== 1 || typeof receipt.remote_plugin_id !== "string")
20688
20823
  throw new Error("Invalid reviewed app-only parent identity");
20689
- const manifests = binding.sources.filter((source) => source.path.startsWith(parent + sep13) && source.path.endsWith(join30(sep13, ".codex-plugin/plugin.json")));
20824
+ const manifests = binding.sources.filter((source) => source.path.startsWith(parent + sep14) && source.path.endsWith(join31(sep14, ".codex-plugin/plugin.json")));
20690
20825
  if (!manifests.length)
20691
20826
  throw new Error("App-only bootstrap lacks prior full manifests");
20692
20827
  for (const source of manifests)
@@ -20694,10 +20829,10 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
20694
20829
  return { role: "app-only", pluginId: `${parts[1]}@${parts[0]}`, namespace: parts[1], pluginParent: parent, remotePluginId: receipt.remote_plugin_id };
20695
20830
  }).sort((a, b) => a.pluginParent.localeCompare(b.pluginParent));
20696
20831
  const reviewedHookParents = (options.reviewedHookParents ?? []).map((parent) => {
20697
- const parts = relative9(cacheRoot, parent).split(sep13);
20698
- if (!isAbsolute24(parent) || resolve22(parent) !== parent || parts.length !== 2 || parts.some((part) => !/^[A-Za-z0-9_-]{1,64}$/.test(part)) || controls.some((control) => control.pluginParent === parent) || appOnlyParents.some((role) => role.pluginParent === parent))
20832
+ const parts = relative9(cacheRoot, parent).split(sep14);
20833
+ if (!isAbsolute24(parent) || resolve23(parent) !== parent || parts.length !== 2 || parts.some((part) => !/^[A-Za-z0-9_-]{1,64}$/.test(part)) || controls.some((control) => control.pluginParent === parent) || appOnlyParents.some((role) => role.pluginParent === parent))
20699
20834
  throw new Error("Unsupported reviewed native-hook parent role");
20700
- const receiptPath = join30(parent, ".codex-remote-plugin-install.json"), receiptStat = lstatSync27(receiptPath, { throwIfNoEntry: false });
20835
+ const receiptPath = join31(parent, ".codex-remote-plugin-install.json"), receiptStat = lstatSync28(receiptPath, { throwIfNoEntry: false });
20701
20836
  let identity5;
20702
20837
  if (receiptStat) {
20703
20838
  if (!receiptStat.isFile() || receiptStat.isSymbolicLink() || receiptStat.size > 16 * 1024)
@@ -20734,11 +20869,11 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
20734
20869
  for (const version2 of versions) {
20735
20870
  if (!/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/.test(version2))
20736
20871
  throw new Error("Invalid reviewed native-hook version identity");
20737
- const root = join30(parent, version2), stat2 = lstatSync27(root, { throwIfNoEntry: false });
20872
+ const root = join31(parent, version2), stat2 = lstatSync28(root, { throwIfNoEntry: false });
20738
20873
  if (!stat2?.isDirectory() || stat2.isSymbolicLink() || realpathSync12(root) !== root)
20739
20874
  throw new Error("Invalid reviewed native-hook materialization");
20740
- unchangedReviewedHookSource(join30(root, ".codex-plugin/plugin.json"));
20741
- unchangedReviewedHookSource(join30(root, "hooks/hooks.json"));
20875
+ unchangedReviewedHookSource(join31(root, ".codex-plugin/plugin.json"));
20876
+ unchangedReviewedHookSource(join31(root, "hooks/hooks.json"));
20742
20877
  }
20743
20878
  return identity5;
20744
20879
  }).sort((a, b) => a.pluginParent.localeCompare(b.pluginParent));
@@ -20748,10 +20883,10 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
20748
20883
  assertCodexSemanticCacheHookSafety(cacheRoot, parents2, configPath, hookRootAlias);
20749
20884
  const supersededSources = binding.sources.filter((source) => codexSemanticCacheSourceRole(source, cacheRoot, parents2, reviewedHookParents.map((parent) => parent.pluginParent)));
20750
20885
  for (const source of supersededSources)
20751
- if ([".app.json", ".mcp.json"].includes(source.path.split(sep13).at(-1)))
20886
+ if ([".app.json", ".mcp.json"].includes(source.path.split(sep14).at(-1)))
20752
20887
  unchangedFullSource(source.path);
20753
20888
  const supersededDirectories = (binding.directories ?? []).filter((directory2) => codexSemanticCacheDirectoryRole(directory2.path, cacheRoot));
20754
- if (!supersededDirectories.some((directory2) => directory2.path === cacheRoot) || (binding.directories ?? []).some((directory2) => cacheRoot.startsWith(directory2.path + sep13)))
20889
+ if (!supersededDirectories.some((directory2) => directory2.path === cacheRoot) || (binding.directories ?? []).some((directory2) => cacheRoot.startsWith(directory2.path + sep14)))
20755
20890
  throw new Error("Cache upgrade requires exact cache directory coverage without an unproved ancestor projection");
20756
20891
  const rules = Bun.TOML.parse(settings).skills?.config ?? [];
20757
20892
  const witness = captureCodexSemanticCacheWitness({ cacheRoot, controls, appOnlyParents, reviewedHookParents, rules, read: boundedRead });
@@ -20774,8 +20909,8 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
20774
20909
  dataDir: dataDir2,
20775
20910
  profileId: policy.profileId,
20776
20911
  nativeSkills: [],
20777
- changes: [{ path: join30(dataDir2, "agent-policy.json"), before: snapshot2.text, after }],
20778
- observedPolicy: { path: join30(dataDir2, "agent-policy.json"), before: snapshot2.text },
20912
+ changes: [{ path: join31(dataDir2, "agent-policy.json"), before: snapshot2.text, after }],
20913
+ observedPolicy: { path: join31(dataDir2, "agent-policy.json"), before: snapshot2.text },
20779
20914
  observedSettings: { path: configPath, before: settings },
20780
20915
  discoveryBefore: [replacement],
20781
20916
  discoveryAfter: [replacement],
@@ -20788,7 +20923,7 @@ function planCodexSemanticCacheWitnessUpgrade(options) {
20788
20923
  function planReviewedArtifactMigration(options) {
20789
20924
  if (options.kind !== "codex-native-catalog" || !/^[a-f0-9]{64}$/.test(options.expectedPolicySha256) || !/^[a-f0-9]{64}$/.test(options.expectedSourceSha256))
20790
20925
  throw new Error("REVIEW_ARTIFACT_INVALID");
20791
- const home = resolve22(options.home ?? homedir2()), dataDir2 = resolve22(options.dataDir ?? getDataDirReadOnly());
20926
+ const home = resolve23(options.home ?? homedir2()), dataDir2 = resolve23(options.dataDir ?? getDataDirReadOnly());
20792
20927
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
20793
20928
  if (!snapshot2 || sha4(snapshot2.text) !== options.expectedPolicySha256)
20794
20929
  throw new Error("REVIEW_ARTIFACT_POLICY_CHANGED");
@@ -20805,8 +20940,8 @@ function planReviewedArtifactMigration(options) {
20805
20940
  dataDir: dataDir2,
20806
20941
  profileId: snapshot2.value.profileId,
20807
20942
  nativeSkills: [],
20808
- changes: [...retained.changes, { path: join30(dataDir2, "agent-policy.json"), before: snapshot2.text, after: serializeManagedSkillPolicy(next) }],
20809
- observedPolicy: { path: join30(dataDir2, "agent-policy.json"), before: snapshot2.text },
20943
+ changes: [...retained.changes, { path: join31(dataDir2, "agent-policy.json"), before: snapshot2.text, after: serializeManagedSkillPolicy(next) }],
20944
+ observedPolicy: { path: join31(dataDir2, "agent-policy.json"), before: snapshot2.text },
20810
20945
  reviewArtifactSources: retained.observed,
20811
20946
  retainedReviewArtifacts: retained.changes,
20812
20947
  reviewArtifactMigration: { ...options, home, dataDir: dataDir2 },
@@ -20832,18 +20967,18 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
20832
20967
  throw new Error("Native identity source changed after planning");
20833
20968
  if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
20834
20969
  throw new Error("Native settings changed after witness planning");
20835
- const policyPath = join30(plan.dataDir, "agent-policy.json");
20970
+ const policyPath = join31(plan.dataDir, "agent-policy.json");
20836
20971
  assertSafePath(policyPath);
20837
- const currentText = (path) => resolve22(path) === resolve22(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
20972
+ const currentText = (path) => resolve23(path) === resolve23(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
20838
20973
  readManagedSkillPolicySnapshot(plan.dataDir);
20839
20974
  for (const change of plan.changes)
20840
- if (resolve22(change.path) === resolve22(policyPath))
20975
+ if (resolve23(change.path) === resolve23(policyPath))
20841
20976
  parseManagedSkillPolicy(change.after);
20842
20977
  const aliases = plan.rootAliases ?? [];
20843
20978
  assertCurrent();
20844
20979
  recheckRootAliases(aliases);
20845
20980
  for (const agent of plan.retainedReviewChecks?.agents ?? [])
20846
- assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
20981
+ assertRetainedReviewState(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
20847
20982
  if (plan.reviewArtifactMigration) {
20848
20983
  const verified = planReviewedArtifactMigration(plan.reviewArtifactMigration);
20849
20984
  if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.discoveryAfter) !== JSON.stringify(plan.discoveryAfter))
@@ -20882,10 +21017,12 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
20882
21017
  for (const change of plan.changes)
20883
21018
  if (currentText(change.path) !== change.before)
20884
21019
  throw new Error(`Configuration changed after planning: ${change.path}`);
20885
- const backupRoot = join30(plan.dataDir, "migration", randomUUID4()), backups = [], written = [], createdDirectories = new Set;
21020
+ const backupRoot = join31(plan.dataDir, "migration", randomUUID4()), backups = [], written = [], createdDirectories = new Set;
20886
21021
  if (!plan.changes.length) {
20887
21022
  assertCurrent();
20888
21023
  recheckRootAliases(aliases);
21024
+ for (const agent of plan.retainedReviewChecks?.agents ?? [])
21025
+ assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
20889
21026
  return { changed: [], backups, ...aliases.length ? { rootAliases: aliases } : {} };
20890
21027
  }
20891
21028
  assertSafePath(backupRoot);
@@ -20893,9 +21030,9 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
20893
21030
  chmodSync(backupRoot, 448);
20894
21031
  for (const [index, change] of plan.changes.entries())
20895
21032
  if (change.before !== null) {
20896
- const backup = join30(backupRoot, `${index}-${sha4(change.path).slice(0, 12)}.backup`);
21033
+ const backup = join31(backupRoot, `${index}-${sha4(change.path).slice(0, 12)}.backup`);
20897
21034
  writeFileSync6(backup, change.before, { mode: 384, flag: "wx" });
20898
- if (readFileSync18(backup, "utf8") !== change.before)
21035
+ if (readFileSync19(backup, "utf8") !== change.before)
20899
21036
  throw new Error("Native configuration preservation readback failed");
20900
21037
  backups.push(backup);
20901
21038
  }
@@ -20905,7 +21042,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
20905
21042
  recheckRootAliases(aliases);
20906
21043
  if (currentText(change.path) !== change.before)
20907
21044
  throw new Error(`Configuration changed after planning: ${change.path}`);
20908
- for (let directory2 = dirname21(change.path);!existsSync9(directory2); directory2 = dirname21(directory2))
21045
+ for (let directory2 = dirname22(change.path);!existsSync9(directory2); directory2 = dirname22(directory2))
20909
21046
  createdDirectories.add(directory2);
20910
21047
  const after = change.after;
20911
21048
  const immutable = plan.retainedReviewArtifacts?.find((item) => item.path === change.path);
@@ -20935,7 +21072,9 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
20935
21072
  }
20936
21073
  for (const agent of plan.managedAgentChecks?.agents ?? [])
20937
21074
  assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.projectDir ?? plan.managedAgentChecks.home });
20938
- atomicWrite2(join30(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha4(change.before), afterHash: sha4(change.after) })), backups, ...plan.cacheWitnessUpgrade ? { cacheWitnessUpgrade: plan.cacheWitnessUpgrade } : {}, ...plan.retainedReviewArtifacts?.length ? { retainedReviewArtifacts: plan.retainedReviewArtifacts.map((item) => ({ path: item.path, ...item.artifact })) } : {}, ...aliases.length ? { rootAliases: aliases } : {} }) + `
21075
+ for (const agent of plan.retainedReviewChecks?.agents ?? [])
21076
+ assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
21077
+ atomicWrite2(join31(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha4(change.before), afterHash: sha4(change.after) })), backups, ...plan.cacheWitnessUpgrade ? { cacheWitnessUpgrade: plan.cacheWitnessUpgrade } : {}, ...plan.retainedReviewArtifacts?.length ? { retainedReviewArtifacts: plan.retainedReviewArtifacts.map((item) => ({ path: item.path, ...item.artifact })) } : {}, ...aliases.length ? { rootAliases: aliases } : {} }) + `
20939
21078
  `);
20940
21079
  } catch (error) {
20941
21080
  assertCurrent();
@@ -20986,11 +21125,11 @@ function writeArchiveJournal(path, value) {
20986
21125
  closeSync17(descriptor);
20987
21126
  descriptor = undefined;
20988
21127
  renameSync4(temporary, path);
20989
- syncArchiveDirectory(dirname21(path));
21128
+ syncArchiveDirectory(dirname22(path));
20990
21129
  } finally {
20991
21130
  if (descriptor !== undefined)
20992
21131
  closeSync17(descriptor);
20993
- if (lstatSync27(temporary, { throwIfNoEntry: false }))
21132
+ if (lstatSync28(temporary, { throwIfNoEntry: false }))
20994
21133
  unlinkSync5(temporary);
20995
21134
  }
20996
21135
  }
@@ -20998,7 +21137,7 @@ function archiveNativeSkills(inventory, options) {
20998
21137
  const selected = options.targetManifest ? selectNativeMigrationTargets(inventory, options.targetManifest) : inventory.filter((entry) => !entry.bridge && !entry.system && (entry.vendor ? options.includeVendor : entry.managed || options.includeUnmanaged));
20999
21138
  const roots = selected.flatMap((entry) => {
21000
21139
  const root = (entry.rootAlias?.agent === "codex" ? entry.rootAlias.alias : undefined) ?? codexCorpusRootForPath(entry.path);
21001
- const owner2 = entry.codexHome ?? (entry.agent === "codex" && entry.bridgeHome ? join30(entry.bridgeHome, ".codex") : undefined);
21140
+ const owner2 = entry.codexHome ?? (entry.agent === "codex" && entry.bridgeHome ? join31(entry.bridgeHome, ".codex") : undefined);
21002
21141
  if (!root && !owner2 && (entry.agent === "codex" || isCodexSkillDiscoveryInput(entry.path)))
21003
21142
  throw new Error("CODEX_CORPUS_ROOT_UNVERIFIED");
21004
21143
  return [...new Set([root, owner2].filter((value) => value !== undefined))];
@@ -21013,7 +21152,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
21013
21152
  recheckRootAliases(aliases);
21014
21153
  for (const entry of inventory.filter((entry2) => entry2.bridge)) {
21015
21154
  const adapter = AGENT_ADAPTERS[entry.agent];
21016
- const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join30(nativeAgentRoot(entry.bridgeHome, entry.agent), CLI_BRIDGE_NAME), aliases) : undefined;
21155
+ const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join31(nativeAgentRoot(entry.bridgeHome, entry.agent), CLI_BRIDGE_NAME), aliases) : undefined;
21017
21156
  if (!expected || treeHash(entry.path) !== entry.hash || !isOwnedCliBridge(entry.path, [expected]))
21018
21157
  throw new Error(`Skills bridge changed after planning: ${entry.path}`);
21019
21158
  }
@@ -21024,11 +21163,11 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
21024
21163
  const targetManifest = options.targetManifest ? { schema: options.targetManifest.schema, digest: options.targetManifest.digest, targetCount: options.targetManifest.targets.length } : undefined;
21025
21164
  if (!selected.length)
21026
21165
  return { entries: [], ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
21027
- const operationId = randomUUID4(), archiveRoot = join30(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join30(archiveRoot, "receipt.json");
21166
+ const operationId = randomUUID4(), archiveRoot = join31(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join31(archiveRoot, "receipt.json");
21028
21167
  const entries = selected.map((entry, index) => ({
21029
- source: entry.vendor ? join30(entry.path, "SKILL.md") : entry.path,
21030
- archive: join30(archiveRoot, `${index}-${sha4(entry.path).slice(0, 12)}`),
21031
- hash: entry.vendor ? sha4(readNativeBytes(join30(entry.path, "SKILL.md"))) : entry.hash,
21168
+ source: entry.vendor ? join31(entry.path, "SKILL.md") : entry.path,
21169
+ archive: join31(archiveRoot, `${index}-${sha4(entry.path).slice(0, 12)}`),
21170
+ hash: entry.vendor ? sha4(readNativeBytes(join31(entry.path, "SKILL.md"))) : entry.hash,
21032
21171
  ...entry.vendor ? { discoveryOnly: true } : {},
21033
21172
  status: "planned"
21034
21173
  }));
@@ -21036,7 +21175,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
21036
21175
  const moved = [];
21037
21176
  const verifyArchive = (entry) => {
21038
21177
  assertSafePath(entry.archive);
21039
- const stat2 = lstatSync27(entry.archive);
21178
+ const stat2 = lstatSync28(entry.archive);
21040
21179
  if (entry.discoveryOnly ? !stat2.isFile() : !stat2.isDirectory())
21041
21180
  throw new Error("Archive type changed");
21042
21181
  if ((entry.discoveryOnly ? sha4(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
@@ -21044,14 +21183,14 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
21044
21183
  };
21045
21184
  assertSafePath(archiveRoot);
21046
21185
  const created = [];
21047
- for (let path = archiveRoot;!existsSync9(path); path = dirname21(path))
21186
+ for (let path = archiveRoot;!existsSync9(path); path = dirname22(path))
21048
21187
  created.push(path);
21049
21188
  mkdirSync8(archiveRoot, { recursive: true, mode: 448 });
21050
21189
  try {
21051
21190
  for (const directory2 of created)
21052
21191
  syncArchiveDirectory(directory2);
21053
21192
  if (created.length)
21054
- syncArchiveDirectory(dirname21(created.at(-1)));
21193
+ syncArchiveDirectory(dirname22(created.at(-1)));
21055
21194
  writeArchiveJournal(receiptPath, journal);
21056
21195
  for (const [index, entry] of selected.entries()) {
21057
21196
  assertCurrent();
@@ -21064,7 +21203,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
21064
21203
  writeArchiveJournal(receiptPath, journal);
21065
21204
  renameSync4(move.source, move.archive);
21066
21205
  moved.push(move);
21067
- syncArchiveDirectory(dirname21(move.source));
21206
+ syncArchiveDirectory(dirname22(move.source));
21068
21207
  syncArchiveDirectory(archiveRoot);
21069
21208
  verifyArchive(move);
21070
21209
  move.status = "archived";
@@ -21087,7 +21226,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
21087
21226
  recheckRootAliases(aliases);
21088
21227
  assertSafePath(entry.source);
21089
21228
  assertSafePath(entry.archive);
21090
- if (lstatSync27(entry.source, { throwIfNoEntry: false })) {
21229
+ if (lstatSync28(entry.source, { throwIfNoEntry: false })) {
21091
21230
  entry.status = "recovery-required";
21092
21231
  entry.conflict = "source-occupied";
21093
21232
  continue;
@@ -21101,10 +21240,10 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
21101
21240
  }
21102
21241
  if (entry.discoveryOnly) {
21103
21242
  linkSync3(entry.archive, entry.source);
21104
- syncArchiveDirectory(dirname21(entry.source));
21243
+ syncArchiveDirectory(dirname22(entry.source));
21105
21244
  unlinkSync5(entry.archive);
21106
21245
  } else {
21107
- if (lstatSync27(entry.source, { throwIfNoEntry: false })) {
21246
+ if (lstatSync28(entry.source, { throwIfNoEntry: false })) {
21108
21247
  entry.status = "recovery-required";
21109
21248
  entry.conflict = "source-occupied";
21110
21249
  continue;
@@ -21112,7 +21251,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
21112
21251
  renameSync4(entry.archive, entry.source);
21113
21252
  }
21114
21253
  entry.status = "restored";
21115
- syncArchiveDirectory(dirname21(entry.source));
21254
+ syncArchiveDirectory(dirname22(entry.source));
21116
21255
  syncArchiveDirectory(archiveRoot);
21117
21256
  } catch {
21118
21257
  entry.status = "recovery-required";
@@ -21135,12 +21274,12 @@ function classifiedCodexPluginCacheCopy(entry, cache, read2, admit) {
21135
21274
  try {
21136
21275
  if (entry.agent !== "codex" || !entry.vendor || entry.bridge || entry.system)
21137
21276
  return false;
21138
- const document2 = join30(entry.path, "SKILL.md");
21277
+ const document2 = join31(entry.path, "SKILL.md");
21139
21278
  const inside = relative9(cache, entry.path);
21140
- if (!inside || isAbsolute24(inside) || inside.split(sep13).some((segment) => segment === ".."))
21279
+ if (!inside || isAbsolute24(inside) || inside.split(sep14).some((segment) => segment === ".."))
21141
21280
  return false;
21142
21281
  assertSafePath(entry.path);
21143
- if (!lstatSync27(cache).isDirectory() || !lstatSync27(entry.path).isDirectory() || !lstatSync27(document2).isFile())
21282
+ if (!lstatSync28(cache).isDirectory() || !lstatSync28(entry.path).isDirectory() || !lstatSync28(document2).isFile())
21144
21283
  return false;
21145
21284
  if (realpathSync12.native(cache) !== cache || realpathSync12.native(entry.path) !== entry.path || realpathSync12.native(document2) !== document2)
21146
21285
  return false;
@@ -21159,7 +21298,7 @@ function isInertCodexPluginCacheCopy(entry, cache, allowedHostPaths, read2) {
21159
21298
  }
21160
21299
  function isInertCodexVendorPluginCopy(entry, cache, rules, read2) {
21161
21300
  try {
21162
- if (entry.managed || lstatSync27(join30(entry.path, ".hasna-skills.json"), { throwIfNoEntry: false }))
21301
+ if (entry.managed || lstatSync28(join31(entry.path, ".hasna-skills.json"), { throwIfNoEntry: false }))
21163
21302
  return false;
21164
21303
  const marketplace = codexVendorMarketplace(cache, entry.path);
21165
21304
  if (!marketplace)
@@ -21177,7 +21316,7 @@ function isInertCodexVendorPluginCopy(entry, cache, rules, read2) {
21177
21316
  try {
21178
21317
  return realpathSync12.native(path);
21179
21318
  } catch {
21180
- return resolve22(path);
21319
+ return resolve23(path);
21181
21320
  }
21182
21321
  };
21183
21322
  const matches = (rule) => typeof rule?.name === "string" && rule.name.trim() === identity5.name || typeof rule?.path === "string" && [document2, entry.path].includes(canonical9(rule.path));
@@ -21197,12 +21336,78 @@ function nativeSkillPathList(paths, invisible = false) {
21197
21336
  const remaining = paths.length - shown.length;
21198
21337
  return `${shown.join(", ")}${remaining ? `; ${remaining} more` : ""}`;
21199
21338
  }
21339
+ function nativeSkillRemediation(agent, blocking, context) {
21340
+ const quote2 = (value) => nativeSkillPathList([value], true);
21341
+ const codex = agent === "codex";
21342
+ const system = blocking.filter((entry) => codex && entry.system === true);
21343
+ const cacheCopies = blocking.filter((entry) => codex && !system.includes(entry) && entry.path.startsWith(context.cache + sep14));
21344
+ const nameRule = (entry) => {
21345
+ if (context.strict || codexVendorMarketplace(context.cache, entry.path) === null)
21346
+ return null;
21347
+ if (context.rules !== undefined && !Array.isArray(context.rules))
21348
+ return null;
21349
+ try {
21350
+ const identity5 = classifyCodexPluginCacheDocument(join31(entry.path, "SKILL.md"), context.cache, context.read);
21351
+ if (!identity5)
21352
+ return null;
21353
+ const rules = [...context.rules ?? [], { name: identity5.name, enabled: false }];
21354
+ return isInertCodexVendorPluginCopy(entry, context.cache, rules, context.read) ? identity5.name : null;
21355
+ } catch {
21356
+ return null;
21357
+ }
21358
+ };
21359
+ const named = cacheCopies.flatMap((entry) => {
21360
+ const name = nameRule(entry);
21361
+ return name ? [{ entry, name }] : [];
21362
+ });
21363
+ const unnamed = cacheCopies.filter((entry) => !named.some((item) => item.entry === entry));
21364
+ const refresh = unnamed.filter((entry) => context.strict || context.toleratedChange || context.enrolled(entry));
21365
+ const byPath = unnamed.filter((entry) => !refresh.includes(entry));
21366
+ const otherVendor = blocking.filter((entry) => entry.vendor && !system.includes(entry) && !cacheCopies.includes(entry));
21367
+ const user = blocking.filter((entry) => !entry.vendor && !system.includes(entry) && !cacheCopies.includes(entry));
21368
+ const install = (catalog) => {
21369
+ const fresh = [...context.automatic ? [] : ["discovery review"], ...catalog ? ["native catalog"] : []];
21370
+ return `skills hook install --agent codex${catalog ? " --codex-native-catalog <reviewed-catalog>" : ""}${context.automatic ? "" : " --discovery-inputs <reviewed-file>"} --apply${fresh.length ? `, with a fresh ${fresh.join(" and ")}` : ""}`;
21371
+ };
21372
+ const followUp = (rows, entries, removesRules = false) => context.automatic || removesRules || !context.keepsConfig(rows) ? ` ${context.automatic ? "This automatic binding" : "The reviewed binding"} records that file, so follow the edit with ${install(entries.some(context.enrolled))}.` : "";
21373
+ const row = (selector) => `[[skills.config]]
21374
+ ${selector}
21375
+ enabled = false
21376
+ `;
21377
+ const steps = [];
21378
+ if (user.length)
21379
+ steps.push(`For user and project copies, review skills migrate native --agent ${agent} --project <working-directory> --include-unmanaged${context.aliases ? " --allow-root-aliases" : ""} --json before continuing; it inventories that directory and its ancestors. Use --apply after reviewing the archive plan.`);
21380
+ if (named.length) {
21381
+ const names = [...new Set(named.map((item) => item.name))];
21382
+ const rows = names.map((name) => row(`name = ${JSON.stringify(name)}`));
21383
+ steps.push(`Codex refreshes OpenAI marketplace copies on its own, so disable each in Codex instead: add [[skills.config]] with name = ${nativeSkillPathList(names, true)}${names.length > 1 ? " (one rule per name)" : ""} and enabled = false to ${quote2(context.config)}. Codex then never loads it and the Codex prompt hook accepts it.${followUp(rows, named.map((item) => item.entry))}`);
21384
+ }
21385
+ if (byPath.length) {
21386
+ const rules = Array.isArray(context.rules) ? context.rules : [];
21387
+ const enabling = rules.some((rule) => rule?.enabled !== false && (typeof rule?.name === "string" || typeof rule?.path === "string" && byPath.some((entry) => [entry.path, join31(entry.path, "SKILL.md")].includes(resolve23(rule.path)))));
21388
+ const rows = byPath.map((entry) => row(`path = ${JSON.stringify(join31(entry.path, "SKILL.md"))}`));
21389
+ steps.push(`For other Codex plugin-cache copies, add [[skills.config]] with the exact path of each SKILL.md and enabled = false to ${quote2(context.config)}, or remove the plugin in Codex.${enabling ? " First remove the [[skills.config]] rules there that enable skills; no path rule counts while one does." : ""}${followUp(rows, byPath, enabling)}`);
21390
+ }
21391
+ if (system.length || refresh.length) {
21392
+ const reasons = [
21393
+ ...system.length ? ["Codex system skills are never archived by skills migrate native"] : [],
21394
+ ...context.strict && refresh.length ? ["this check, unlike the live Codex prompt hook, does not accept a hand-added Codex name rule for a plugin-cache copy"] : [],
21395
+ ...!context.strict && context.toleratedChange && refresh.length ? ["plugin-cache copies keep their exact-path exemption only while the recorded configuration is unchanged"] : [],
21396
+ ...refresh.some(context.enrolled) ? ["only a fresh native catalog review exempts a skill of a plugin Skills has enrolled"] : []
21397
+ ];
21398
+ const sentence = reasons.join("; ");
21399
+ steps.push(`${sentence[0].toUpperCase()}${sentence.slice(1)}, so refresh the binding instead: ${install(refresh.some(context.enrolled))}. It disables and records each copy.`);
21400
+ }
21401
+ if (otherVendor.length)
21402
+ steps.push(`For other vendor copies, review skills migrate native --agent ${agent} --project <working-directory> --include-vendor --discovery-inputs <reviewed-file> --json; --include-vendor requires a reviewed --discovery-inputs file when a configured plugin declares hooks.`);
21403
+ return steps.join(" ");
21404
+ }
21200
21405
  function assertManagedAgentBridge(agent, options = {}) {
21201
21406
  return assertManagedAgentBridgeWithDiscovery(agent, options);
21202
21407
  }
21203
- function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
21204
- const home = resolve22(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
21205
- assertSafePath(join30(dataDir2, "agent-policy.json"));
21408
+ function assertManagedBridgeCustody(agent, options = {}, provenDiscovery) {
21409
+ const home = resolve23(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
21410
+ assertSafePath(join31(dataDir2, "agent-policy.json"));
21206
21411
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
21207
21412
  if (!snapshot2)
21208
21413
  throw new Error("NATIVE_SKILL_DRIFT: install the Skills bridge with skills hook install");
@@ -21213,11 +21418,10 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
21213
21418
  if (!Array.isArray(aliases))
21214
21419
  throw new Error("NATIVE_SKILL_DRIFT: invalid root alias binding");
21215
21420
  recheckRootAliases(aliases);
21216
- const expected = canonicalAgentPath(join30(nativeAgentRoot(home, agent, options.projectDir ?? process.cwd()), CLI_BRIDGE_NAME), aliases);
21421
+ const expected = canonicalAgentPath(join31(nativeAgentRoot(home, agent, options.projectDir ?? process.cwd()), CLI_BRIDGE_NAME), aliases);
21217
21422
  if (!isOwnedCliBridge(expected, [expected]))
21218
21423
  throw new Error("NATIVE_SKILL_DRIFT: the native Skills bridge is missing or modified; repair it before continuing");
21219
21424
  const roots = projectAncestorDirectories([options.projectDir ?? process.cwd(), ...options.projectDirs ?? [], ...agent === "hermes" && process.env.TERMINAL_CWD ? [process.cwd()] : []]);
21220
- const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes", "sumi"].includes(agent) && entry.path.includes(`${sep13}.agents${sep13}skills${sep13}`) || ["opencode", "sumi"].includes(agent) && entry.agent === "claude";
21221
21425
  const discovery = provenDiscovery ?? binding.discovery?.[agent];
21222
21426
  assertProjectDiscovery(agent, [...roots], home, (path) => canonicalAgentPath(path, aliases), discovery);
21223
21427
  for (const source of discovery?.sources ?? [])
@@ -21232,16 +21436,35 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
21232
21436
  throw new Error("NATIVE_SKILL_DRIFT: the native hook command/profile binding is missing");
21233
21437
  if (options.profileId !== undefined && options.profileId !== profile)
21234
21438
  throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
21439
+ return { home, dataDir: dataDir2, binding, aliases, expected, roots, discovery, configPath, config, command, profile };
21440
+ }
21441
+ function assertUnrepairableBridgeProtection(custody, agent) {
21442
+ if (agent !== "hermes")
21443
+ return;
21444
+ const { home, dataDir: dataDir2, binding, config, command, profile } = custody;
21445
+ const supervisor = binding.supervisors?.hermes;
21446
+ if (supervisor?.path !== join31(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha4(renderHermesSupervisor(command, profile)))
21447
+ throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
21448
+ assertHermesProtection(home, config, command, profile, supervisor);
21449
+ }
21450
+ function assertRetainedReviewState(agent, options) {
21451
+ const custody = assertManagedBridgeCustody(agent, options);
21452
+ assertUnrepairableBridgeProtection(custody, agent);
21453
+ assertManagedNativeDiscovery(custody, agent, options);
21454
+ }
21455
+
21456
+ class NativeSkillCopiesDriftError extends Error {
21457
+ }
21458
+ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
21459
+ const custody = assertManagedBridgeCustody(agent, options, provenDiscovery);
21460
+ const { home, aliases, expected, discovery, config, command, profile } = custody;
21235
21461
  if (agent === "hermes") {
21236
- const supervisor = binding.supervisors?.hermes;
21237
- if (supervisor?.path !== join30(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha4(renderHermesSupervisor(command, profile)))
21238
- throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
21239
- assertHermesProtection(home, config, command, profile, supervisor);
21462
+ assertUnrepairableBridgeProtection(custody, agent);
21240
21463
  } else if (agent === "sumi") {
21241
- if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || !isManagedSumiPlugin(readOptional(join30(sumiConfigDirectory(home, process.env, options.projectDir ?? process.cwd()), "plugins", "skills-cli.js")), command, profile))
21464
+ if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || !isManagedSumiPlugin(readOptional(join31(sumiConfigDirectory(home, process.env, options.projectDir ?? process.cwd()), "plugins", "skills-cli.js")), command, profile))
21242
21465
  throw new Error("NATIVE_SKILL_DRIFT: Sumi bridge protection changed; run skills hook install");
21243
21466
  } else if (agent === "opencode") {
21244
- if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(join30(home, ".config", "opencode", "plugins", "skills-cli.js")) !== renderOpenCodePlugin(command, profile))
21467
+ if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(join31(home, ".config", "opencode", "plugins", "skills-cli.js")) !== renderOpenCodePlugin(command, profile))
21245
21468
  throw new Error("NATIVE_SKILL_DRIFT: OpenCode bridge protection changed; run skills hook install");
21246
21469
  } else {
21247
21470
  const required = {};
@@ -21258,8 +21481,23 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
21258
21481
  if (agent === "gemini" && (config.hooksConfig?.enabled === false || config.skills?.enabled !== true || !["antigravity-support", "skill-creator"].every((name) => config.skills?.disabled?.includes(name)) || config.skills?.disabled?.includes(CLI_BRIDGE_NAME)))
21259
21482
  throw new Error("NATIVE_SKILL_DRIFT: Gemini bridge or bundled-skill protection changed; run skills hook install");
21260
21483
  }
21261
- const codexPath = canonicalAgentPath(join30(home, ".codex", "config.toml"), aliases);
21262
- const vendorCache = canonicalAgentPath(join30(home, ".codex", "plugins", "cache"), aliases);
21484
+ if (agent === "codex") {
21485
+ const codexConfig = Bun.TOML.parse(readOptional(canonicalAgentPath(join31(home, ".codex", "config.toml"), aliases)) ?? "");
21486
+ if (codexConfig.skills?.bundled?.enabled !== false)
21487
+ throw new Error("NATIVE_SKILL_DRIFT: Codex bundled skill reseeding is not disabled (skills.bundled.enabled); run skills hook install");
21488
+ const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join31(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
21489
+ if ([...setting(expected), ...(codexConfig.skills?.config ?? []).filter((entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME)].some((entry) => entry.enabled === false))
21490
+ throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
21491
+ }
21492
+ if (agent === "gemini" && discovery && !discovery.builtinNames?.every((name) => config.skills.disabled.includes(name)))
21493
+ throw new Error("NATIVE_SKILL_DRIFT: an installed Gemini builtin is not disabled");
21494
+ return assertManagedNativeDiscovery(custody, agent, options);
21495
+ }
21496
+ function assertManagedNativeDiscovery(custody, agent, options) {
21497
+ const { home, dataDir: dataDir2, binding, aliases, expected, roots, discovery } = custody;
21498
+ const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes", "sumi"].includes(agent) && entry.path.includes(`${sep14}.agents${sep14}skills${sep14}`) || ["opencode", "sumi"].includes(agent) && entry.agent === "claude";
21499
+ const codexPath = canonicalAgentPath(join31(home, ".codex", "config.toml"), aliases);
21500
+ const vendorCache = canonicalAgentPath(join31(home, ".codex", "plugins", "cache"), aliases);
21263
21501
  const witnessTolerance = agent === "codex" && options.codexPromptHook === true ? {
21264
21502
  changes: [],
21265
21503
  acceptsInput: (source) => source.path === codexPath && codexVendorConfigChangeOnly(source, vendorCache),
@@ -21268,49 +21506,42 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
21268
21506
  if (options.codexDiscoveryRecovery && (agent !== "codex" || options.codexDiscoveryRecovery.configPath !== codexPath))
21269
21507
  throw new Error("NATIVE_SKILL_DRIFT: Codex trust recovery names a different configuration root");
21270
21508
  const codexConfig = agent === "codex" ? Bun.TOML.parse(readOptional(codexPath) ?? "") : {};
21271
- if (agent === "codex" && codexConfig.skills?.bundled?.enabled !== false)
21272
- throw new Error("NATIVE_SKILL_DRIFT: Codex bundled skill reseeding is not disabled (skills.bundled.enabled); run skills hook install");
21273
- const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join30(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
21274
- if (agent === "codex" && [...setting(expected), ...(codexConfig.skills?.config ?? []).filter((entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME)].some((entry) => entry.enabled === false))
21275
- throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
21276
- if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join30(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), codexConfig.skills?.config ?? []))
21509
+ if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join31(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), codexConfig.skills?.config ?? []))
21277
21510
  throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
21278
21511
  const disabledPlugin = (entry) => {
21279
21512
  if (agent !== "codex" || !entry.vendor)
21280
21513
  return false;
21281
- const document2 = join30(entry.path, "SKILL.md"), cache = canonicalAgentPath(join30(home, ".codex/plugins/cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024));
21514
+ const document2 = join31(entry.path, "SKILL.md"), cache = canonicalAgentPath(join31(home, ".codex/plugins/cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024));
21282
21515
  return isReviewedCodexPluginInactive(document2, cache, binding.codexInactivePlugins ?? [], codexConfig.plugins, codexConfig.skills?.config ?? [], read2) || isReviewedCodexPluginSkillDisabled(document2, cache, binding.codexPluginSkills ?? [], codexConfig.skills?.config, read2);
21283
21516
  };
21284
- const inertVendorCopy = (entry) => agent === "codex" && options.codexPromptHook === true && entry.vendor && entry.path.startsWith(vendorCache + sep13) && isInertCodexVendorPluginCopy(entry, vendorCache, codexConfig.skills?.config, (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
21285
- const vendorOnlyCacheCopy = (entry) => (witnessTolerance?.changes.length ?? 0) > 0 && entry.path.startsWith(vendorCache + sep13);
21517
+ const inertVendorCopy = (entry) => agent === "codex" && options.codexPromptHook === true && entry.vendor && entry.path.startsWith(vendorCache + sep14) && isInertCodexVendorPluginCopy(entry, vendorCache, codexConfig.skills?.config, (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
21518
+ const vendorOnlyCacheCopy = (entry) => (witnessTolerance?.changes.length ?? 0) > 0 && entry.path.startsWith(vendorCache + sep14);
21286
21519
  const disabledPaths = new Set(disabledCodexSkillPaths(codexConfig, aliases));
21287
21520
  const disabledVendorSkill = (entry) => {
21288
21521
  if (agent !== "codex" || !entry.vendor)
21289
21522
  return false;
21290
21523
  if (vendorOnlyCacheCopy(entry))
21291
21524
  return false;
21292
- if ((binding.codexPluginSkills ?? []).some((control) => entry.path.startsWith(control.pluginParent + sep13)))
21525
+ if ((binding.codexPluginSkills ?? []).some((control) => entry.path.startsWith(control.pluginParent + sep14)))
21293
21526
  return disabledPlugin(entry);
21294
21527
  if (disabledPlugin(entry))
21295
21528
  return true;
21296
21529
  if (!disabledPaths.has(entry.path))
21297
21530
  return false;
21298
- if (entry.path.startsWith(canonicalAgentPath(join30(home, ".codex", "plugins", "cache"), aliases) + sep13))
21531
+ if (entry.path.startsWith(canonicalAgentPath(join31(home, ".codex", "plugins", "cache"), aliases) + sep14))
21299
21532
  return true;
21300
- const runtimeCache = canonicalAgentPath(join30(home, ".cache", "codex-runtimes"), aliases);
21301
- if (entry.path.startsWith(runtimeCache + sep13) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep13)))
21533
+ const runtimeCache = canonicalAgentPath(join31(home, ".cache", "codex-runtimes"), aliases);
21534
+ if (entry.path.startsWith(runtimeCache + sep14) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep14)))
21302
21535
  return true;
21303
- const bundledMarketplaces = canonicalAgentPath(join30(home, ".codex", ".tmp", "bundled-marketplaces"), aliases);
21304
- if (entry.path.startsWith(bundledMarketplaces + sep13) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep13)))
21536
+ const bundledMarketplaces = canonicalAgentPath(join31(home, ".codex", ".tmp", "bundled-marketplaces"), aliases);
21537
+ if (entry.path.startsWith(bundledMarketplaces + sep14) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep14)))
21305
21538
  return true;
21306
- if (!entry.path.startsWith(canonicalAgentPath(join30(home, ".codex", "skills", ".system"), aliases) + sep13))
21539
+ if (!entry.path.startsWith(canonicalAgentPath(join31(home, ".codex", "skills", ".system"), aliases) + sep14))
21307
21540
  return false;
21308
21541
  return Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
21309
21542
  };
21310
21543
  if (!discovery || discovery.agent !== agent)
21311
21544
  throw new Error("NATIVE_SKILL_DRIFT: native discovery coverage is missing; run skills hook install");
21312
- if (agent === "gemini" && !discovery.builtinNames?.every((name) => config.skills.disabled.includes(name)))
21313
- throw new Error("NATIVE_SKILL_DRIFT: an installed Gemini builtin is not disabled");
21314
21545
  try {
21315
21546
  verifyAgentDiscovery(agent === "codex" ? { ...discovery, codexDisabledPluginSkills: binding.codexPluginSkills ?? [] } : discovery, options.codexDiscoveryRecovery, aliases, witnessTolerance);
21316
21547
  if (agent === "sumi" && discovery.method === "reviewed")
@@ -21325,7 +21556,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
21325
21556
  let installationInputRoots = [];
21326
21557
  if (agent === "codex") {
21327
21558
  try {
21328
- installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(join30(home, ".codex/plugins/cache"), aliases), discovery.codexInstallationInputs?.plugins ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
21559
+ installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(join31(home, ".codex/plugins/cache"), aliases), discovery.codexInstallationInputs?.plugins ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
21329
21560
  } catch {
21330
21561
  throw new Error("NATIVE_SKILL_DRIFT: native installation input identity changed; review discovery and catalog");
21331
21562
  }
@@ -21335,7 +21566,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
21335
21566
  let adapterRefusal, inert = [];
21336
21567
  if (agent === "codex" && unexpected.length && options.codexNativePolicy) {
21337
21568
  const { inspector, helperRunner, deadlineMs, ...envelope } = options.codexNativePolicy;
21338
- const cache = canonicalAgentPath(join30(home, ".codex", "plugins", "cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), bridgeDocument = join30(expected, "SKILL.md");
21569
+ const cache = canonicalAgentPath(join31(home, ".codex", "plugins", "cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), bridgeDocument = join31(expected, "SKILL.md");
21339
21570
  try {
21340
21571
  const claimed = parseCodexNativeHookEnvelope(envelope, bridgeDocument).policy;
21341
21572
  inert = unexpected.filter((entry) => isInertCodexPluginCacheCopy(entry, cache, claimed.allowedHostPaths, read2));
@@ -21354,7 +21585,18 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
21354
21585
  }
21355
21586
  if (unexpected.length > inert.length + inertVendor.length) {
21356
21587
  const blocking = unexpected.filter((entry) => !inertVendor.includes(entry));
21357
- throw new Error(`NATIVE_SKILL_DRIFT: ${blocking.length} unexpected native skill copies were found: ${nativeSkillPathList(blocking.map((entry) => entry.path))}. Review skills migrate native --agent ${agent} --project <working-directory> --include-unmanaged --include-vendor --json before continuing; it inventories that directory and its ancestors. Use --apply after reviewing the archive plan.${adapterRefusal ? ` Native policy adapter refused: ${adapterRefusal}` : ""}`);
21588
+ const remediation = nativeSkillRemediation(agent, blocking, { cache: vendorCache, config: codexPath, aliases: aliases.length > 0, strict: options.codexPromptHook !== true, automatic: discovery.method === "automatic", toleratedChange: (witnessTolerance?.changes.length ?? 0) > 0, rules: codexConfig.skills?.config, enrolled: (entry) => (binding.codexPluginSkills ?? []).some((control) => entry.path.startsWith(control.pluginParent + sep14)), keepsConfig: (rows) => {
21589
+ try {
21590
+ const text2 = readOptional(codexPath) ?? "";
21591
+ return discoveryTextKeepsWitness(discovery, codexPath, `${text2.trimEnd()}${text2.trim() ? `
21592
+
21593
+ ` : ""}${rows.join(`
21594
+ `)}`);
21595
+ } catch {
21596
+ return false;
21597
+ }
21598
+ }, read: (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)) });
21599
+ throw new NativeSkillCopiesDriftError(`NATIVE_SKILL_DRIFT: ${blocking.length} unexpected native skill copies were found: ${nativeSkillPathList(blocking.map((entry) => entry.path))}. ${remediation}${adapterRefusal ? ` Native policy adapter refused: ${adapterRefusal}` : ""}`);
21358
21600
  }
21359
21601
  recheckRootAliases(aliases);
21360
21602
  const discoveryChanges = (witnessTolerance?.changes ?? []).filter((change, index, all) => all.findIndex((other) => other.kind === change.kind && other.path === change.path) === index);
@@ -21378,11 +21620,11 @@ function isSkillsOwnershipMarker(marker) {
21378
21620
  return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
21379
21621
  }
21380
21622
  function hasSkillsOwnershipMarker(dir) {
21381
- const path = join31(dir, SYNC_MARKER_FILE);
21623
+ const path = join32(dir, SYNC_MARKER_FILE);
21382
21624
  try {
21383
- if (!lstatSync28(path).isFile())
21625
+ if (!lstatSync29(path).isFile())
21384
21626
  return false;
21385
- const marker = JSON.parse(readFileSync19(path, "utf8"));
21627
+ const marker = JSON.parse(readFileSync20(path, "utf8"));
21386
21628
  return isSkillsOwnershipMarker(marker);
21387
21629
  } catch {
21388
21630
  return false;
@@ -21402,9 +21644,9 @@ function resolveSyncAgents(arg) {
21402
21644
  function agentGlobalSkillsDir(agent, homeDir2 = homedir3()) {
21403
21645
  switch (agent) {
21404
21646
  case "opencode":
21405
- return join31(homeDir2, ".config", "opencode", "skills");
21647
+ return join32(homeDir2, ".config", "opencode", "skills");
21406
21648
  default:
21407
- return join31(homeDir2, `.${agent}`, "skills");
21649
+ return join32(homeDir2, `.${agent}`, "skills");
21408
21650
  }
21409
21651
  }
21410
21652
  function adaptSkillMdForAgent(skillMd, agent) {
@@ -21463,7 +21705,7 @@ function resolveSyncCorpus(options = {}) {
21463
21705
  function packageSourceRoots(source) {
21464
21706
  const roots = [];
21465
21707
  for (const sub of ["skills"]) {
21466
- const candidate = join31(source, sub);
21708
+ const candidate = join32(source, sub);
21467
21709
  if (existsSync10(candidate) && isDirectory(candidate))
21468
21710
  roots.push(candidate);
21469
21711
  }
@@ -21479,10 +21721,10 @@ function containsSkillDirectories(path) {
21479
21721
  return false;
21480
21722
  }
21481
21723
  return entries.some((entry) => {
21482
- const candidate = join31(path, entry);
21724
+ const candidate = join32(path, entry);
21483
21725
  if (!isDirectory(candidate))
21484
21726
  return false;
21485
- return existsSync10(join31(candidate, "SKILL.md")) || existsSync10(join31(candidate, "skill.json")) || existsSync10(join31(candidate, "package.json"));
21727
+ return existsSync10(join32(candidate, "SKILL.md")) || existsSync10(join32(candidate, "skill.json")) || existsSync10(join32(candidate, "package.json"));
21486
21728
  });
21487
21729
  }
21488
21730
  function isDirectory(path) {
@@ -21522,7 +21764,7 @@ function syncSkillsToAgents(options = {}) {
21522
21764
  actions.push({
21523
21765
  skill: name,
21524
21766
  agent,
21525
- path: join31(agentGlobalSkillsDir(agent, homeDir2), name, "SKILL.md"),
21767
+ path: join32(agentGlobalSkillsDir(agent, homeDir2), name, "SKILL.md"),
21526
21768
  action: "skip",
21527
21769
  reason: "not found in this machine's corpus"
21528
21770
  });
@@ -21554,7 +21796,7 @@ function syncSkillsToAgents(options = {}) {
21554
21796
  }
21555
21797
  function writeManagedAgentSkill(params) {
21556
21798
  const homeDir2 = params.homeDir ?? homedir3();
21557
- const dir = join31(agentGlobalSkillsDir(params.agent, homeDir2), params.skill);
21799
+ const dir = join32(agentGlobalSkillsDir(params.agent, homeDir2), params.skill);
21558
21800
  const result = writeManagedSkillDir(dir, params.skillMd, {
21559
21801
  skill: params.skill,
21560
21802
  source: params.source,
@@ -21577,8 +21819,8 @@ function writeManagedSkillDir(dir, skillMd, options) {
21577
21819
  return withCodexCorpusWrite(root ? [root] : [], (assertCurrent) => writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent), options);
21578
21820
  }
21579
21821
  function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
21580
- const skillMdPath = join31(dir, "SKILL.md");
21581
- const markerPath = join31(dir, SYNC_MARKER_FILE);
21822
+ const skillMdPath = join32(dir, "SKILL.md");
21823
+ const markerPath = join32(dir, SYNC_MARKER_FILE);
21582
21824
  const dirExists = existsSync10(dir);
21583
21825
  const managed = hasSkillsOwnershipMarker(dir);
21584
21826
  const hasSkillMd = existsSync10(skillMdPath);
@@ -21599,7 +21841,7 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
21599
21841
  if (dirExists && managed && hasSkillMd && !options.force && isPointerSkillMd(skillMd)) {
21600
21842
  let existingIsStub = false;
21601
21843
  try {
21602
- existingIsStub = isPointerSkillMd(readFileSync19(skillMdPath, "utf-8"));
21844
+ existingIsStub = isPointerSkillMd(readFileSync20(skillMdPath, "utf-8"));
21603
21845
  } catch {
21604
21846
  existingIsStub = false;
21605
21847
  }
@@ -21614,13 +21856,13 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
21614
21856
  const action = dirExists ? "update" : "create";
21615
21857
  if (options.dryRun)
21616
21858
  return { action, path: skillMdPath };
21617
- const parentDir = dirname22(dir);
21859
+ const parentDir = dirname23(dir);
21618
21860
  mkdirSync9(parentDir, { recursive: true });
21619
- const transactionDir = mkdtempSync(join31(parentDir, `.hasna-skills-write-${basename12(dir)}-`));
21620
- const candidateDir = join31(transactionDir, "candidate");
21621
- const backupDir = join31(transactionDir, "backup");
21622
- const candidateSkillMdPath = join31(candidateDir, "SKILL.md");
21623
- const candidateMarkerPath = join31(candidateDir, SYNC_MARKER_FILE);
21861
+ const transactionDir = mkdtempSync(join32(parentDir, `.hasna-skills-write-${basename12(dir)}-`));
21862
+ const candidateDir = join32(transactionDir, "candidate");
21863
+ const backupDir = join32(transactionDir, "backup");
21864
+ const candidateSkillMdPath = join32(candidateDir, "SKILL.md");
21865
+ const candidateMarkerPath = join32(candidateDir, SYNC_MARKER_FILE);
21624
21866
  const marker = {
21625
21867
  managedBy: SYNC_MARKER_MANAGED_BY,
21626
21868
  skill: options.skill,
@@ -21649,7 +21891,7 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
21649
21891
  assertCurrent();
21650
21892
  renameDirectory(candidateDir, dir);
21651
21893
  assertCurrent();
21652
- if (readFileSync19(skillMdPath, "utf8") !== (skillMd.endsWith(`
21894
+ if (readFileSync20(skillMdPath, "utf8") !== (skillMd.endsWith(`
21653
21895
  `) ? skillMd : `${skillMd}
21654
21896
  `))
21655
21897
  throw new Error("Managed skill readback failed");
@@ -21678,12 +21920,12 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
21678
21920
  return { action, path: skillMdPath };
21679
21921
  }
21680
21922
  function removeManagedAgentSkill(skill, agent, homeDir2 = homedir3(), options = {}) {
21681
- const dir = join31(agentGlobalSkillsDir(agent, homeDir2), skill);
21923
+ const dir = join32(agentGlobalSkillsDir(agent, homeDir2), skill);
21682
21924
  const root = codexCorpusRootForPath(dir);
21683
21925
  return withCodexCorpusWrite(root ? [root] : [], () => removeManagedAgentSkillUnlocked(skill, agent, homeDir2, options), options);
21684
21926
  }
21685
21927
  function removeManagedAgentSkillUnlocked(skill, agent, homeDir2 = homedir3(), options = {}) {
21686
- const dir = join31(agentGlobalSkillsDir(agent, homeDir2), skill);
21928
+ const dir = join32(agentGlobalSkillsDir(agent, homeDir2), skill);
21687
21929
  if (!hasSkillsOwnershipMarker(dir))
21688
21930
  return false;
21689
21931
  rmSync2(dir, { recursive: true, force: true });
@@ -21691,9 +21933,9 @@ function removeManagedAgentSkillUnlocked(skill, agent, homeDir2 = homedir3(), op
21691
21933
  }
21692
21934
  function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = false) {
21693
21935
  if (kind === undefined || kind === "instruction" || preferBundledDocs) {
21694
- const skillMdPath = join31(skillPath, "SKILL.md");
21936
+ const skillMdPath = join32(skillPath, "SKILL.md");
21695
21937
  if (existsSync10(skillMdPath))
21696
- return readFileSync19(skillMdPath, "utf-8");
21938
+ return readFileSync20(skillMdPath, "utf-8");
21697
21939
  }
21698
21940
  return pointerSkillMd(name, description);
21699
21941
  }
@@ -21733,8 +21975,8 @@ function resolveSkillAlias(name) {
21733
21975
  }
21734
21976
 
21735
21977
  // src/lib/project-state.ts
21736
- import { existsSync as existsSync11, mkdirSync as mkdirSync10, readFileSync as readFileSync20, writeFileSync as writeFileSync8 } from "fs";
21737
- import { join as join32 } from "path";
21978
+ import { existsSync as existsSync11, mkdirSync as mkdirSync10, readFileSync as readFileSync21, writeFileSync as writeFileSync8 } from "fs";
21979
+ import { join as join33 } from "path";
21738
21980
  var VALID_PIN_SOURCES = [
21739
21981
  "official",
21740
21982
  "custom",
@@ -21749,17 +21991,17 @@ var SKILLS_PROJECT_DIR = ".skills";
21749
21991
  var PROJECT_CONFIG_FILE = "project.json";
21750
21992
  var DEFAULT_EXPORT_DIR = ".skills/exports";
21751
21993
  function getProjectStateDir(targetDir = process.cwd()) {
21752
- return join32(targetDir, SKILLS_PROJECT_DIR);
21994
+ return join33(targetDir, SKILLS_PROJECT_DIR);
21753
21995
  }
21754
21996
  function getProjectConfigPath(targetDir = process.cwd()) {
21755
- return join32(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
21997
+ return join33(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
21756
21998
  }
21757
21999
  function loadProjectConfig(targetDir = process.cwd()) {
21758
22000
  const path = getProjectConfigPath(targetDir);
21759
22001
  if (!existsSync11(path))
21760
22002
  return null;
21761
22003
  try {
21762
- return normalizeProjectConfig(JSON.parse(readFileSync20(path, "utf-8")));
22004
+ return normalizeProjectConfig(JSON.parse(readFileSync21(path, "utf-8")));
21763
22005
  } catch {
21764
22006
  return null;
21765
22007
  }
@@ -21881,7 +22123,7 @@ function getSkillPath(name) {
21881
22123
  const extensionPath = findExtensionSkillPath(skillName);
21882
22124
  if (extensionPath)
21883
22125
  return extensionPath;
21884
- return join33(getPortableSkillsRoot(), skillName);
22126
+ return join34(getPortableSkillsRoot(), skillName);
21885
22127
  }
21886
22128
  function getCanonicalSkillName(name) {
21887
22129
  return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
@@ -21944,9 +22186,9 @@ function createLocalSkillManifest(name, generateSkillMd) {
21944
22186
  if (!existsSync12(sourcePath))
21945
22187
  return null;
21946
22188
  let skillMd = "";
21947
- const skillMdPath = join33(sourcePath, "SKILL.md");
22189
+ const skillMdPath = join34(sourcePath, "SKILL.md");
21948
22190
  if (existsSync12(skillMdPath)) {
21949
- skillMd = readFileSync21(skillMdPath, "utf-8");
22191
+ skillMd = readFileSync23(skillMdPath, "utf-8");
21950
22192
  } else if (generateSkillMd) {
21951
22193
  skillMd = generateSkillMd(name) ?? "";
21952
22194
  } else {
@@ -22018,16 +22260,16 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
22018
22260
  const base = projectDir || process.cwd();
22019
22261
  switch (agent) {
22020
22262
  case "pi":
22021
- return scope === "project" ? join33(base, ".pi", "skills") : join33(homedir4(), ".pi", "agent", "skills");
22263
+ return scope === "project" ? join34(base, ".pi", "skills") : join34(homedir4(), ".pi", "agent", "skills");
22022
22264
  case "opencode":
22023
- return scope === "project" ? join33(base, ".opencode", "skills") : join33(homedir4(), ".config", "opencode", "skills");
22265
+ return scope === "project" ? join34(base, ".opencode", "skills") : join34(homedir4(), ".config", "opencode", "skills");
22024
22266
  default:
22025
- return scope === "project" ? join33(base, `.${agent}`, "skills") : join33(homedir4(), `.${agent}`, "skills");
22267
+ return scope === "project" ? join34(base, `.${agent}`, "skills") : join34(homedir4(), `.${agent}`, "skills");
22026
22268
  }
22027
22269
  }
22028
22270
  function getAgentSkillPath(name, agent, scope = "global", projectDir) {
22029
22271
  const skillName = normalizeSkillName(getCanonicalSkillName(name));
22030
- return join33(getAgentSkillsDir(agent, scope, projectDir), skillName);
22272
+ return join34(getAgentSkillsDir(agent, scope, projectDir), skillName);
22031
22273
  }
22032
22274
  function installSkillForAgent(name, options, generateSkillMd) {
22033
22275
  assertNativeExportAllowed();
@@ -22070,9 +22312,9 @@ function removeSkillForAgentUnlocked(name, options) {
22070
22312
  }
22071
22313
  function resolveAgentSkillMd(name, generateSkillMd) {
22072
22314
  const sourcePath = getSkillPath(name);
22073
- const skillMdPath = join33(sourcePath, "SKILL.md");
22315
+ const skillMdPath = join34(sourcePath, "SKILL.md");
22074
22316
  if (existsSync12(skillMdPath))
22075
- return readFileSync21(skillMdPath, "utf-8");
22317
+ return readFileSync23(skillMdPath, "utf-8");
22076
22318
  if (generateSkillMd)
22077
22319
  return generateSkillMd(name);
22078
22320
  return generateMinimalSkillMd(name);
@@ -22105,7 +22347,7 @@ function generateMinimalSkillMd(name) {
22105
22347
  "---",
22106
22348
  ""
22107
22349
  ].filter(Boolean);
22108
- const fallbackDoc = readFileIfExists(join33(sourcePath, "README.md")) || readFileIfExists(join33(sourcePath, "CLAUDE.md"));
22350
+ const fallbackDoc = readFileIfExists(join34(sourcePath, "README.md")) || readFileIfExists(join34(sourcePath, "CLAUDE.md"));
22109
22351
  if (fallbackDoc)
22110
22352
  return `${frontmatter.join(`
22111
22353
  `)}${fallbackDoc.trim()}
@@ -22124,18 +22366,18 @@ skills run ${canonicalName}
22124
22366
  `;
22125
22367
  }
22126
22368
  function readBundledSkillVersion(name) {
22127
- const pkgPath = join33(getSkillPath(name), "package.json");
22369
+ const pkgPath = join34(getSkillPath(name), "package.json");
22128
22370
  if (!existsSync12(pkgPath))
22129
22371
  return "unknown";
22130
22372
  try {
22131
- const pkg = JSON.parse(readFileSync21(pkgPath, "utf-8"));
22373
+ const pkg = JSON.parse(readFileSync23(pkgPath, "utf-8"));
22132
22374
  return pkg.version || "unknown";
22133
22375
  } catch {
22134
22376
  return "unknown";
22135
22377
  }
22136
22378
  }
22137
22379
  function readFileIfExists(path) {
22138
- return existsSync12(path) ? readFileSync21(path, "utf-8") : null;
22380
+ return existsSync12(path) ? readFileSync23(path, "utf-8") : null;
22139
22381
  }
22140
22382
  function loadProjectConfigCompat(targetDir) {
22141
22383
  return loadProjectConfig(targetDir);
@@ -22179,11 +22421,11 @@ function selectSkillDoc(docs, file) {
22179
22421
  function isInstructionSkillDir(skillPath, meta) {
22180
22422
  if (meta?.kind === "instruction")
22181
22423
  return true;
22182
- const skillMdPath = join34(skillPath, "SKILL.md");
22424
+ const skillMdPath = join35(skillPath, "SKILL.md");
22183
22425
  if (!existsSync13(skillMdPath))
22184
22426
  return false;
22185
22427
  try {
22186
- return parseSkillFrontmatter(readFileSync23(skillMdPath, "utf-8"))?.kind === "instruction";
22428
+ return parseSkillFrontmatter(readFileSync25(skillMdPath, "utf-8"))?.kind === "instruction";
22187
22429
  } catch {
22188
22430
  return false;
22189
22431
  }
@@ -22209,9 +22451,9 @@ function getSkillDocs(name) {
22209
22451
  if (!existsSync13(skillPath))
22210
22452
  return null;
22211
22453
  return {
22212
- skillMd: readIfExists(join34(skillPath, "SKILL.md")),
22213
- readme: readIfExists(join34(skillPath, "README.md")),
22214
- claudeMd: readIfExists(join34(skillPath, "CLAUDE.md"))
22454
+ skillMd: readIfExists(join35(skillPath, "SKILL.md")),
22455
+ readme: readIfExists(join35(skillPath, "README.md")),
22456
+ claudeMd: readIfExists(join35(skillPath, "CLAUDE.md"))
22215
22457
  };
22216
22458
  }
22217
22459
  function getSkillBestDoc(name, file) {
@@ -22227,14 +22469,14 @@ function getSkillRequirements(name) {
22227
22469
  return null;
22228
22470
  const texts = [];
22229
22471
  for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
22230
- const content = readIfExists(join34(skillPath, file));
22472
+ const content = readIfExists(join35(skillPath, file));
22231
22473
  if (content)
22232
22474
  texts.push(content);
22233
22475
  }
22234
22476
  const meta = getSkill(name);
22235
22477
  let dependencies = {};
22236
22478
  try {
22237
- dependencies = JSON.parse(readFileSync23(join34(skillPath, "package.json"), "utf8")).dependencies || {};
22479
+ dependencies = JSON.parse(readFileSync25(join35(skillPath, "package.json"), "utf8")).dependencies || {};
22238
22480
  } catch {}
22239
22481
  return getSkillRequirementsFromContent(name, texts, dependencies, meta);
22240
22482
  }
@@ -22296,13 +22538,13 @@ async function runSkill(name, args, options = {}) {
22296
22538
  error: `Skill '${name}' is an instruction skill (kind: instruction) and is not runnable. Instruction skills are consumed by coding agents via SKILL.md, not executed with 'skills run'.`
22297
22539
  };
22298
22540
  }
22299
- const pkgPath = join34(skillPath, "package.json");
22541
+ const pkgPath = join35(skillPath, "package.json");
22300
22542
  if (!existsSync13(pkgPath)) {
22301
22543
  return { exitCode: 1, error: `No package.json in skill '${name}'` };
22302
22544
  }
22303
22545
  let entryPoint2;
22304
22546
  try {
22305
- const pkg = JSON.parse(readFileSync23(pkgPath, "utf-8"));
22547
+ const pkg = JSON.parse(readFileSync25(pkgPath, "utf-8"));
22306
22548
  if (pkg.bin) {
22307
22549
  const binValues = Object.values(pkg.bin);
22308
22550
  entryPoint2 = binValues[0];
@@ -22316,12 +22558,12 @@ async function runSkill(name, args, options = {}) {
22316
22558
  } catch {
22317
22559
  return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
22318
22560
  }
22319
- const entryPath = join34(skillPath, entryPoint2);
22561
+ const entryPath = join35(skillPath, entryPoint2);
22320
22562
  if (!existsSync13(entryPath)) {
22321
22563
  return { exitCode: 1, error: `Entry point '${entryPoint2}' not found in skill '${name}'` };
22322
22564
  }
22323
22565
  const env = { ...process.env, ...options.env };
22324
- if (!existsSync13(join34(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
22566
+ if (!existsSync13(join35(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
22325
22567
  const failure = await prepareSkillDependencies(skillPath, env, options.preparationTimeoutMs);
22326
22568
  if (failure)
22327
22569
  return failure;
@@ -22399,13 +22641,13 @@ function generateSkillMd(name) {
22399
22641
  "---"
22400
22642
  ].join(`
22401
22643
  `);
22402
- const readme = readIfExists(join34(skillPath, "README.md"));
22403
- const claudeMd = readIfExists(join34(skillPath, "CLAUDE.md"));
22644
+ const readme = readIfExists(join35(skillPath, "README.md"));
22645
+ const claudeMd = readIfExists(join35(skillPath, "CLAUDE.md"));
22404
22646
  let cliCommand = null;
22405
- const pkgPath = join34(skillPath, "package.json");
22647
+ const pkgPath = join35(skillPath, "package.json");
22406
22648
  if (existsSync13(pkgPath)) {
22407
22649
  try {
22408
- const pkg = JSON.parse(readFileSync23(pkgPath, "utf-8"));
22650
+ const pkg = JSON.parse(readFileSync25(pkgPath, "utf-8"));
22409
22651
  if (pkg.bin) {
22410
22652
  const binKeys = Object.keys(pkg.bin);
22411
22653
  if (binKeys.length > 0)
@@ -22479,7 +22721,7 @@ function extractEnvVars(text2) {
22479
22721
  function readIfExists(path) {
22480
22722
  try {
22481
22723
  if (existsSync13(path)) {
22482
- return readFileSync23(path, "utf-8");
22724
+ return readFileSync25(path, "utf-8");
22483
22725
  }
22484
22726
  } catch {}
22485
22727
  return null;
@@ -22631,7 +22873,7 @@ function describeEntries(entries) {
22631
22873
 
22632
22874
  // src/lib/plugin-admission.ts
22633
22875
  function pluginAdmissionRoot() {
22634
- return join35(homedir5(), ".hasna", "skills", "plugin-admission");
22876
+ return join36(homedir5(), ".hasna", "skills", "plugin-admission");
22635
22877
  }
22636
22878
  function validatePluginTarget(value) {
22637
22879
  pluginKeys(value, ["schemaVersion", "pluginId", "registrations", "native", "resolver"]);
@@ -22642,7 +22884,7 @@ function validatePluginTarget(value) {
22642
22884
  for (const registration of value.registrations) {
22643
22885
  pluginKeys(registration, ["scope", "projectPath"]);
22644
22886
  pluginNeed(registration.scope === "user" || registration.scope === "project", "Unsupported plugin registration scope");
22645
- pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" && isAbsolute25(registration.projectPath) && resolve23(registration.projectPath) === registration.projectPath, "Plugin scope requires its exact project path");
22887
+ pluginNeed(registration.scope === "user" ? registration.projectPath === null : typeof registration.projectPath === "string" && isAbsolute25(registration.projectPath) && resolve24(registration.projectPath) === registration.projectPath, "Plugin scope requires its exact project path");
22646
22888
  const key2 = JSON.stringify([registration.scope, registration.projectPath]);
22647
22889
  pluginNeed(!scopes.has(key2), "Duplicate plugin registration scope");
22648
22890
  scopes.add(key2);
@@ -22650,7 +22892,7 @@ function validatePluginTarget(value) {
22650
22892
  for (const key2 of ["native", "resolver"]) {
22651
22893
  pluginKeys(value[key2], key2 === "native" ? ["version", "executable", "digest"] : ["executable", "digest"]);
22652
22894
  pluginText(value[key2].executable);
22653
- pluginNeed(isAbsolute25(value[key2].executable) && resolve23(value[key2].executable) === value[key2].executable, "Plugin executable must have a canonical absolute path");
22895
+ pluginNeed(isAbsolute25(value[key2].executable) && resolve24(value[key2].executable) === value[key2].executable, "Plugin executable must have a canonical absolute path");
22654
22896
  pluginDigest(value[key2].digest);
22655
22897
  }
22656
22898
  pluginNeed(["2.1.274", "2.1.276"].includes(value.native.version), "Plugin admission requires a certified Claude command-source runtime (2.1.274 or 2.1.276)");
@@ -22802,17 +23044,17 @@ function pluginResolverCommand(binding) {
22802
23044
  }
22803
23045
  function root(options) {
22804
23046
  const path = options.storeRoot ?? pluginAdmissionRoot();
22805
- pluginNeed(isAbsolute25(path) && resolve23(path) === path, "Invalid plugin admission store root");
23047
+ pluginNeed(isAbsolute25(path) && resolve24(path) === path, "Invalid plugin admission store root");
22806
23048
  return path;
22807
23049
  }
22808
23050
  function pluginBindingPath(storeRoot, id) {
22809
23051
  pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
22810
- return join35(storeRoot, "bindings", `${id}.json`);
23052
+ return join36(storeRoot, "bindings", `${id}.json`);
22811
23053
  }
22812
23054
  function pluginReceiptPath(storeRoot, id, digest2) {
22813
23055
  pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
22814
23056
  pluginDigest(digest2);
22815
- return join35(storeRoot, "receipts", id, `${digest2.slice(7)}.json`);
23057
+ return join36(storeRoot, "receipts", id, `${digest2.slice(7)}.json`);
22816
23058
  }
22817
23059
  function readPluginBinding(storeRoot, id) {
22818
23060
  const value = readPluginJson(pluginBindingPath(storeRoot, id));
@@ -22832,7 +23074,7 @@ function readPluginAdmissionReceipt(storeRoot, id, digest2) {
22832
23074
  pluginNeed(receipt.plan.bindingId === id && receipt.plan.planDigest === digest2, "Plugin receipt identity differs from its filename");
22833
23075
  const binding = readPluginBinding(storeRoot, id);
22834
23076
  pluginNeed(canonicalJson(binding) === canonicalJson(receipt.plan.binding) && receipt.plan.sourceCommand === pluginResolverCommand(binding), "Plugin receipt binding changed");
22835
- pluginNeed(receipt.materializedPath === join35(storeRoot, "objects", id, digest2.slice(7)), "Plugin receipt points outside its immutable materialization");
23077
+ pluginNeed(receipt.materializedPath === join36(storeRoot, "objects", id, digest2.slice(7)), "Plugin receipt points outside its immutable materialization");
22836
23078
  pluginText(receipt.admittedAt, 64);
22837
23079
  pluginNeed(Number.isFinite(Date.parse(receipt.admittedAt)), "Invalid plugin admission time");
22838
23080
  return receipt;
@@ -22973,7 +23215,7 @@ async function admitPlugin(spec, profileId, target, approvedDigest, approvedEvid
22973
23215
  const { plan, projection } = await candidate(spec, profileId, target, options);
22974
23216
  pluginNeed(plan.planDigest === approvedDigest, "The plugin plan changed since review; obtain and review a new plan");
22975
23217
  pluginNeed(plan.evidenceDigest === approvedEvidenceDigest, "The plugin routing evidence changed since review; obtain and review a new plan");
22976
- const store = root(options), materializedPath = join35(store, "objects", plan.bindingId, plan.planDigest.slice(7));
23218
+ const store = root(options), materializedPath = join36(store, "objects", plan.bindingId, plan.planDigest.slice(7));
22977
23219
  materializePluginTree(materializedPath, projection.files);
22978
23220
  writePluginJsonImmutable(pluginBindingPath(store, plan.bindingId), plan.binding);
22979
23221
  const path = pluginReceiptPath(store, plan.bindingId, plan.planDigest), existing = readPluginJson(path);
@@ -23007,21 +23249,21 @@ function validateManagedPluginWitnesses(value) {
23007
23249
  pluginNeed(typeof item.bindingId === "string" && /^[a-f0-9]{64}$/.test(item.bindingId) && !seen.has(item.bindingId), "Invalid or duplicate managed plugin witness");
23008
23250
  seen.add(item.bindingId);
23009
23251
  pluginText(item.storeRoot);
23010
- pluginNeed(isAbsolute26(item.storeRoot) && resolve24(item.storeRoot) === item.storeRoot, "Invalid plugin receipt store path");
23252
+ pluginNeed(isAbsolute26(item.storeRoot) && resolve25(item.storeRoot) === item.storeRoot, "Invalid plugin receipt store path");
23011
23253
  }
23012
23254
  }
23013
23255
  function safe5(path) {
23014
23256
  pluginText(path);
23015
- pluginNeed(isAbsolute26(path) && resolve24(path) === path, "Invalid plugin registry path");
23016
- for (let at = path;; at = dirname23(at)) {
23017
- pluginNeed(!lstatSync29(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
23018
- if (at === dirname23(at))
23257
+ pluginNeed(isAbsolute26(path) && resolve25(path) === path, "Invalid plugin registry path");
23258
+ for (let at = path;; at = dirname24(at)) {
23259
+ pluginNeed(!lstatSync30(at, { throwIfNoEntry: false })?.isSymbolicLink(), "Plugin registry path traverses a symlink");
23260
+ if (at === dirname24(at))
23019
23261
  break;
23020
23262
  }
23021
23263
  }
23022
23264
  function readRegistry(path) {
23023
23265
  safe5(path);
23024
- const initial = lstatSync29(path);
23266
+ const initial = lstatSync30(path);
23025
23267
  pluginNeed(initial.isFile() && initial.size <= 1024 * 1024, "Plugin registry is not a bounded regular file");
23026
23268
  const fd = openSync18(path, constants19.O_RDONLY | constants19.O_NOFOLLOW | constants19.O_NONBLOCK);
23027
23269
  try {
@@ -23035,7 +23277,7 @@ function readRegistry(path) {
23035
23277
  break;
23036
23278
  length += count;
23037
23279
  }
23038
- const after = fstatSync18(fd), current = lstatSync29(path);
23280
+ const after = fstatSync18(fd), current = lstatSync30(path);
23039
23281
  safe5(path);
23040
23282
  pluginNeed(length <= 1024 * 1024 && length === opened.size && after.size === opened.size && after.mtimeMs === opened.mtimeMs && after.ctimeMs === opened.ctimeMs && current.dev === opened.dev && current.ino === opened.ino && current.mtimeMs === opened.mtimeMs && current.ctimeMs === opened.ctimeMs, "Plugin registry changed during read");
23041
23283
  let value;
@@ -23065,7 +23307,7 @@ function hashManagedPluginRegistry(path, managed) {
23065
23307
  seen.add(target.pluginId);
23066
23308
  const rows = plugins[target.pluginId];
23067
23309
  pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
23068
- const [name, market] = target.pluginId.split("@"), cacheRoot = join36(dirname23(path), "cache", market, name);
23310
+ const [name, market] = target.pluginId.split("@"), cacheRoot = join37(dirname24(path), "cache", market, name);
23069
23311
  const scopes = new Set, approvals = new Map, currentFiles = new Map;
23070
23312
  plugins[target.pluginId] = rows.map((row) => {
23071
23313
  pluginNeed(pluginObject(row), "Invalid managed plugin registration");
@@ -23078,7 +23320,7 @@ function hashManagedPluginRegistry(path, managed) {
23078
23320
  pluginText(row.sourceProducerPath);
23079
23321
  pluginText(row.lastUpdated, 64);
23080
23322
  pluginNeed(Number.isFinite(Date.parse(row.lastUpdated)), "Invalid managed plugin update timestamp");
23081
- pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join36(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
23323
+ pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join37(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
23082
23324
  const approval = (producer) => {
23083
23325
  pluginText(producer);
23084
23326
  const digest2 = `sha256:${basename13(producer)}`;
@@ -23108,7 +23350,7 @@ function hashManagedPluginRegistry(path, managed) {
23108
23350
  return Object.fromEntries(Object.entries(row).filter(([key2]) => !["version", "installPath", "sourceProducerPath", "previousProducerPaths", "lastUpdated"].includes(key2)));
23109
23351
  });
23110
23352
  safe5(cacheRoot);
23111
- const before = lstatSync29(cacheRoot);
23353
+ const before = lstatSync30(cacheRoot);
23112
23354
  pluginNeed(before.isDirectory(), "Invalid managed plugin cache root");
23113
23355
  const directory2 = opendirSync5(cacheRoot), names = [];
23114
23356
  try {
@@ -23121,17 +23363,17 @@ function hashManagedPluginRegistry(path, managed) {
23121
23363
  directory2.closeSync();
23122
23364
  }
23123
23365
  for (const version2 of names.sort()) {
23124
- const files = pluginFileWitnesses(snapshotPluginTree(join36(cacheRoot, version2), { nativeRuntime: `claude-${target.native.version}` }));
23366
+ const files = pluginFileWitnesses(snapshotPluginTree(join37(cacheRoot, version2), { nativeRuntime: `claude-${target.native.version}` }));
23125
23367
  verifiedBytes += files.reduce((total, file) => total + file.size, 0);
23126
23368
  pluginNeed(verifiedBytes <= 256 * 1024 * 1024, "Managed plugin verification exceeds its aggregate byte budget");
23127
23369
  const serialized = JSON.stringify(files);
23128
- const expected = currentFiles.get(join36(cacheRoot, version2));
23370
+ const expected = currentFiles.get(join37(cacheRoot, version2));
23129
23371
  pluginNeed(expected === undefined || serialized === expected, "Current native cache differs from its exact registered producer receipt");
23130
23372
  pluginNeed([...approvals.values()].some((receipt) => nativeVersionMatches(version2, receipt) && JSON.stringify(receipt.plan.files) === serialized), "A retained native plugin cache version differs from every approved projection");
23131
23373
  }
23132
23374
  for (const row of rows)
23133
23375
  pluginNeed(names.includes(row.version), "Managed native plugin installation is missing");
23134
- const after = lstatSync29(cacheRoot);
23376
+ const after = lstatSync30(cacheRoot);
23135
23377
  safe5(cacheRoot);
23136
23378
  pluginNeed(before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs, "Managed cache version membership changed during verification");
23137
23379
  }
@@ -23144,24 +23386,24 @@ function captureManagedPluginRegistry(path, managedPlugins) {
23144
23386
 
23145
23387
  // src/lib/claude-marketplace-registry.ts
23146
23388
  import { createHash as createHash28 } from "crypto";
23147
- import { closeSync as closeSync19, constants as constants20, fstatSync as fstatSync19, lstatSync as lstatSync30, openSync as openSync19, readSync as readSync14 } from "fs";
23148
- import { basename as basename14, dirname as dirname24, isAbsolute as isAbsolute27, resolve as resolve25 } from "path";
23389
+ import { closeSync as closeSync19, constants as constants20, fstatSync as fstatSync19, lstatSync as lstatSync31, openSync as openSync19, readSync as readSync14 } from "fs";
23390
+ import { basename as basename14, dirname as dirname25, isAbsolute as isAbsolute27, resolve as resolve26 } from "path";
23149
23391
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
23150
23392
  function need10(value, reason2) {
23151
23393
  if (!value)
23152
23394
  throw new Error(`Claude marketplace registry ${reason2}`);
23153
23395
  }
23154
23396
  function absolutePath4(value) {
23155
- need10(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute27(value) && resolve25(value) === value, "requires a normalized absolute path");
23397
+ need10(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_MARKETPLACE_REGISTRY_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute27(value) && resolve26(value) === value, "requires a normalized absolute path");
23156
23398
  }
23157
23399
  var sameFile2 = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
23158
23400
  function parents2(path) {
23159
23401
  const result = [];
23160
- for (let at = dirname24(path);; at = dirname24(at)) {
23161
- const stat2 = lstatSync30(at, { bigint: true });
23402
+ for (let at = dirname25(path);; at = dirname25(at)) {
23403
+ const stat2 = lstatSync31(at, { bigint: true });
23162
23404
  need10(stat2.isDirectory() && !stat2.isSymbolicLink(), "path traverses a symlink or non-directory");
23163
23405
  result.push([at, stat2]);
23164
- if (at === dirname24(at))
23406
+ if (at === dirname25(at))
23165
23407
  return result;
23166
23408
  }
23167
23409
  }
@@ -23169,7 +23411,7 @@ function readRegistry2(path, budget2) {
23169
23411
  absolutePath4(path);
23170
23412
  need10(basename14(path) === "known_marketplaces.json", "requires known_marketplaces.json");
23171
23413
  need10(Number.isSafeInteger(budget2.remaining) && budget2.remaining >= 0, "has an invalid byte budget");
23172
- const ancestors2 = parents2(path), initial = lstatSync30(path, { bigint: true });
23414
+ const ancestors2 = parents2(path), initial = lstatSync31(path, { bigint: true });
23173
23415
  need10(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
23174
23416
  need10(initial.size <= BigInt(budget2.remaining), "exceeds its aggregate byte limit");
23175
23417
  const fd = openSync19(path, constants20.O_RDONLY | constants20.O_NOFOLLOW | constants20.O_NONBLOCK);
@@ -23184,10 +23426,10 @@ function readRegistry2(path, budget2) {
23184
23426
  break;
23185
23427
  length += count;
23186
23428
  }
23187
- const after = fstatSync19(fd, { bigint: true }), current = lstatSync30(path, { bigint: true });
23429
+ const after = fstatSync19(fd, { bigint: true }), current = lstatSync31(path, { bigint: true });
23188
23430
  need10(current.isFile() && sameFile2(opened, after) && sameFile2(after, current) && BigInt(length) === opened.size, "changed during read");
23189
23431
  for (const [ancestor, before] of ancestors2) {
23190
- const now = lstatSync30(ancestor, { bigint: true });
23432
+ const now = lstatSync31(ancestor, { bigint: true });
23191
23433
  need10(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
23192
23434
  }
23193
23435
  budget2.remaining -= length;
@@ -23380,7 +23622,7 @@ function discoveryExecutable(agent) {
23380
23622
  const found = command ? Bun.which(command) : null;
23381
23623
  if (!command || !found)
23382
23624
  return null;
23383
- const path = resolve26(found), target = realpathOrNull(path);
23625
+ const path = resolve27(found), target = realpathOrNull(path);
23384
23626
  return target === null ? null : { command, path, target };
23385
23627
  }
23386
23628
  function recordedDiscoveryExecutable(agent, recorded) {
@@ -23404,10 +23646,10 @@ function parseConfig(text2, path, toml = false) {
23404
23646
  function safe6(path) {
23405
23647
  if (!isAbsolute28(path) || path.includes("\x00"))
23406
23648
  throw new Error("Expected an absolute native discovery path");
23407
- for (let cursor = path;; cursor = dirname25(cursor)) {
23408
- if (lstatSync31(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
23649
+ for (let cursor = path;; cursor = dirname26(cursor)) {
23650
+ if (lstatSync32(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
23409
23651
  throw new Error(`Refusing symlink native discovery input: ${cursor}`);
23410
- if (dirname25(cursor) === cursor)
23652
+ if (dirname26(cursor) === cursor)
23411
23653
  break;
23412
23654
  }
23413
23655
  }
@@ -23415,19 +23657,19 @@ function read2(path, changes) {
23415
23657
  safe6(path);
23416
23658
  if (changes?.has(path))
23417
23659
  return changes.get(path);
23418
- const stat2 = lstatSync31(path, { throwIfNoEntry: false });
23660
+ const stat2 = lstatSync32(path, { throwIfNoEntry: false });
23419
23661
  if (!stat2)
23420
23662
  return null;
23421
23663
  if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
23422
23664
  throw new Error(`Unsupported or oversized native discovery input: ${path}`);
23423
- return readFileSync25(path, "utf8");
23665
+ return readFileSync26(path, "utf8");
23424
23666
  }
23425
23667
  function assertReviewedClaudeCacheClosures(home, config, review, canonical9) {
23426
23668
  const enabledIds = Object.entries(config.enabledPlugins ?? {}).filter(([, enabled]) => enabled === true).map(([id]) => id);
23427
23669
  if (enabledIds.length === 0)
23428
23670
  return;
23429
- const registryPath = canonical9(join37(home, ".claude/plugins/installed_plugins.json"));
23430
- const cacheAliasRoot = join37(home, ".claude/plugins/cache");
23671
+ const registryPath = canonical9(join38(home, ".claude/plugins/installed_plugins.json"));
23672
+ const cacheAliasRoot = join38(home, ".claude/plugins/cache");
23431
23673
  const cacheRoot = canonical9(cacheAliasRoot);
23432
23674
  const registryText = read2(registryPath);
23433
23675
  if (registryText === null)
@@ -23435,7 +23677,7 @@ function assertReviewedClaudeCacheClosures(home, config, review, canonical9) {
23435
23677
  const registry = parseConfig(registryText, registryPath);
23436
23678
  if (registry.version !== 2 || !registry.plugins || typeof registry.plugins !== "object" || Array.isArray(registry.plugins))
23437
23679
  throw new Error("Reviewed Claude installed plugin registrations are invalid");
23438
- const cachePath = (path) => typeof path === "string" && isAbsolute28(path) && resolve26(path) === path && [cacheAliasRoot, cacheRoot].some((root2) => path === root2 || path.startsWith(root2 + sep14));
23680
+ const cachePath = (path) => typeof path === "string" && isAbsolute28(path) && resolve27(path) === path && [cacheAliasRoot, cacheRoot].some((root2) => path === root2 || path.startsWith(root2 + sep15));
23439
23681
  const activeRoots = new Set;
23440
23682
  for (const id of enabledIds) {
23441
23683
  const rows = registry.plugins[id];
@@ -23447,11 +23689,11 @@ function assertReviewedClaudeCacheClosures(home, config, review, canonical9) {
23447
23689
  if (!row || typeof row !== "object" || Array.isArray(row) || typeof row.installPath !== "string") {
23448
23690
  throw new Error("Reviewed Claude enabled plugin registration requires an object with an installPath string");
23449
23691
  }
23450
- if ([cacheAliasRoot, cacheRoot].some((root2) => row.installPath === root2 || row.installPath.startsWith(root2 + sep14))) {
23692
+ if ([cacheAliasRoot, cacheRoot].some((root2) => row.installPath === root2 || row.installPath.startsWith(root2 + sep15))) {
23451
23693
  if (!cachePath(row.installPath) || row.installPath.length > AGENT_POLICY_LIMITS.pathCharacters || row.version !== undefined && (typeof row.version !== "string" || basename15(row.installPath) !== row.version)) {
23452
23694
  throw new Error("Reviewed Claude discovery has a malformed active cache registration");
23453
23695
  }
23454
- if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute28(row.projectPath) && resolve26(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
23696
+ if (row.scope === "user" && (row.projectPath === undefined || row.projectPath === null)) {} else if (row.scope === "project" && typeof row.projectPath === "string" && isAbsolute28(row.projectPath) && resolve27(row.projectPath) === row.projectPath && row.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters) {} else
23455
23697
  throw new Error("Reviewed Claude discovery has an unsupported active cache registration scope");
23456
23698
  activeRoots.add(canonical9(row.installPath));
23457
23699
  }
@@ -23469,11 +23711,11 @@ function assertReviewedClaudeCacheClosures(home, config, review, canonical9) {
23469
23711
  };
23470
23712
  for (const root2 of activeRoots) {
23471
23713
  safe6(root2);
23472
- const rootStat = lstatSync31(root2, { throwIfNoEntry: false });
23714
+ const rootStat = lstatSync32(root2, { throwIfNoEntry: false });
23473
23715
  if (!rootStat?.isDirectory() || realpathSync13(root2) !== root2)
23474
23716
  throw new Error("Reviewed Claude active cache plugin root is missing or unsafe");
23475
- const manifestPath = join37(root2, ".claude-plugin/plugin.json");
23476
- const manifestStat = lstatSync31(manifestPath, { throwIfNoEntry: false });
23717
+ const manifestPath = join38(root2, ".claude-plugin/plugin.json");
23718
+ const manifestStat = lstatSync32(manifestPath, { throwIfNoEntry: false });
23477
23719
  let manifest;
23478
23720
  if (manifestStat) {
23479
23721
  if (!manifestStat.isFile())
@@ -23488,30 +23730,30 @@ function assertReviewedClaudeCacheClosures(home, config, review, canonical9) {
23488
23730
  } else if (!review.sources.some((source) => source.path === manifestPath && source.hashMode === "path-bytes" && source.sha256 !== null && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined)) {
23489
23731
  throw new Error("Reviewed Claude active cache plugin manifest absence requires a path-bytes witness");
23490
23732
  }
23491
- const roots = new Set([join37(root2, "skills")]);
23492
- if (lstatSync31(join37(root2, "SKILL.md"), { throwIfNoEntry: false })?.isFile())
23733
+ const roots = new Set([join38(root2, "skills")]);
23734
+ if (lstatSync32(join38(root2, "SKILL.md"), { throwIfNoEntry: false })?.isFile())
23493
23735
  roots.add(root2);
23494
23736
  const declared = manifest?.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
23495
23737
  if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
23496
23738
  throw new Error("Reviewed Claude active cache plugin has unsupported skill paths");
23497
23739
  for (const value of declared) {
23498
- const path = resolve26(root2, value);
23499
- if (path !== root2 && !path.startsWith(root2 + sep14))
23740
+ const path = resolve27(root2, value);
23741
+ if (path !== root2 && !path.startsWith(root2 + sep15))
23500
23742
  throw new Error("Reviewed Claude active cache plugin skill path escapes its root");
23501
- roots.add(path.endsWith(`${sep14}SKILL.md`) ? dirname25(path) : path);
23743
+ roots.add(path.endsWith(`${sep15}SKILL.md`) ? dirname26(path) : path);
23502
23744
  }
23503
23745
  for (const skillRoot of roots)
23504
23746
  if (!review.roots.includes(skillRoot))
23505
23747
  throw new Error("Reviewed Claude active cache plugin skill root is not covered");
23506
- const hooksPath = join37(root2, "hooks/hooks.json");
23507
- const hooksStat = lstatSync31(hooksPath, { throwIfNoEntry: false });
23748
+ const hooksPath = join38(root2, "hooks/hooks.json");
23749
+ const hooksStat = lstatSync32(hooksPath, { throwIfNoEntry: false });
23508
23750
  if (hooksStat) {
23509
23751
  if (!hooksStat.isFile())
23510
23752
  throw new Error("Reviewed Claude plugin hook manifest is unsupported");
23511
23753
  requireSource(hooksPath, "hook manifest");
23512
23754
  }
23513
23755
  if (typeof manifest?.hooks === "string")
23514
- requireSource(resolve26(root2, manifest.hooks), "manifest-declared hook source");
23756
+ requireSource(resolve27(root2, manifest.hooks), "manifest-declared hook source");
23515
23757
  }
23516
23758
  }
23517
23759
  function projectNativeDiscoveryFields(text2, format, fields2, path) {
@@ -23564,10 +23806,9 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
23564
23806
  if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5")) {
23565
23807
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
23566
23808
  throw new Error("Codex settings witnesses require exact typed metadata");
23567
- const current = (source.hashMode === "codex-settings-v5" ? captureCodexSettingsV5 : source.hashMode === "codex-settings-v4" ? captureCodexSettingsV4 : source.hashMode === "codex-settings-v3" ? captureCodexSettingsV3 : source.hashMode === "codex-settings-v2" ? captureCodexSettingsV2 : captureCodexSettings)(source.path, budget2).sha256;
23568
- if (current !== source.sha256)
23809
+ if (codexSettingsWitnessMatch(source.path, source.hashMode, source.sha256, budget2) === null)
23569
23810
  throw new NativeDiscoveryWitnessChange("input", source.path, `Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
23570
- return changes?.has(source.path) ? (source.hashMode === "codex-settings-v5" ? hashCodexSettingsReplacementV5 : source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : current;
23811
+ return changes?.has(source.path) ? (source.hashMode === "codex-settings-v5" ? hashCodexSettingsReplacementV5 : source.hashMode === "codex-settings-v4" ? hashCodexSettingsReplacementV4 : source.hashMode === "codex-settings-v3" ? hashCodexSettingsReplacementV3 : source.hashMode === "codex-settings-v2" ? hashCodexSettingsReplacementV2 : hashCodexSettingsReplacement)(changes.get(source.path), budget2) : source.sha256;
23571
23812
  }
23572
23813
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4")) {
23573
23814
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256))
@@ -23643,18 +23884,18 @@ function absentCodexSkillRoots(binding, tolerance) {
23643
23884
  return [];
23644
23885
  if (binding.agent !== "codex" || !Array.isArray(controls) || controls.length > 4096)
23645
23886
  throw new Error("Invalid disabled Codex discovery proof");
23646
- if (binding.method !== "reviewed" || controls.every((control) => lstatSync31(control.pluginParent, { throwIfNoEntry: false })))
23887
+ if (binding.method !== "reviewed" || controls.every((control) => lstatSync32(control.pluginParent, { throwIfNoEntry: false })))
23647
23888
  return [];
23648
- const cache = dirname25(dirname25(controls[0].pluginParent)), config = join37(dirname25(dirname25(cache)), "config.toml");
23889
+ const cache = dirname26(dirname26(controls[0].pluginParent)), config = join38(dirname26(dirname26(cache)), "config.toml");
23649
23890
  const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
23650
23891
  if (!source || !codexConfigWitnessCurrent(source, tolerance))
23651
23892
  throw new Error("Missing current disabled Codex configuration witness");
23652
23893
  const rules = Bun.TOML.parse(read2(config)).skills?.config ?? [];
23653
23894
  return [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && absentDisabledCodexPluginParent(cache, parent, controls, rules));
23654
23895
  }
23655
- function codexDirectHookSelects(cache, root2, configPath = join37(dirname25(dirname25(cache)), "config.toml")) {
23656
- const codex = dirname25(configPath), config = Bun.TOML.parse(read2(configPath) ?? "");
23657
- const hooks = read2(join37(codex, "hooks.json"));
23896
+ function codexDirectHookSelects(cache, root2, configPath = join38(dirname26(dirname26(cache)), "config.toml")) {
23897
+ const codex = dirname26(configPath), config = Bun.TOML.parse(read2(configPath) ?? "");
23898
+ const hooks = read2(join38(codex, "hooks.json"));
23658
23899
  if (hooks !== null)
23659
23900
  hashNativeJsonControls(hooks);
23660
23901
  return JSON.stringify(config.hooks ?? {}).includes(root2) || hooks !== null && JSON.stringify(JSON.parse(hooks)).includes(root2);
@@ -23666,7 +23907,7 @@ function retiredCodexRoots(binding, tolerance) {
23666
23907
  const controls = binding.codexDisabledPluginSkills;
23667
23908
  if (binding.agent !== "codex" || binding.method !== "reviewed" || !Array.isArray(controls) || !controls.length || !Array.isArray(proof.roots) || proof.roots.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.roots).size !== proof.roots.length || !Array.isArray(proof.directories) || proof.parents !== undefined && (!Array.isArray(proof.parents) || proof.parents.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.parents).size !== proof.parents.length) || !proof.roots.length && !proof.parents?.length)
23668
23909
  throw new Error("Invalid retired Codex materialization proof");
23669
- const cache = dirname25(dirname25(controls[0].pluginParent)), config = join37(dirname25(dirname25(cache)), "config.toml");
23910
+ const cache = dirname26(dirname26(controls[0].pluginParent)), config = join38(dirname26(dirname26(cache)), "config.toml");
23670
23911
  const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
23671
23912
  if (!source || !codexConfigWitnessCurrent(source, tolerance))
23672
23913
  throw new Error("Missing current retired Codex configuration witness");
@@ -23674,7 +23915,7 @@ function retiredCodexRoots(binding, tolerance) {
23674
23915
  if ((proof.parents ?? []).some((parent) => codexDirectHookSelects(cache, parent) || !reviewedDisabledCodexPluginParent(cache, parent, controls, rules)) || !reviewedCodexPluginCapabilitiesUnchanged(cache, controls, (path) => read2(path), rules))
23675
23916
  throw new Error("Retired Codex parent capabilities changed");
23676
23917
  for (const root2 of proof.roots) {
23677
- const manifest = binding.sources.find((source2) => source2.path === join37(root2, ".codex-plugin/plugin.json") && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && [undefined, "bytes"].includes(source2.hashMode));
23918
+ const manifest = binding.sources.find((source2) => source2.path === join38(root2, ".codex-plugin/plugin.json") && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && [undefined, "bytes"].includes(source2.hashMode));
23678
23919
  if (!manifest || codexDirectHookSelects(cache, root2) || !reviewedRetiredCodexRoot(cache, root2, controls, rules, (path) => read2(path), true))
23679
23920
  throw new Error("Retired Codex materialization changed");
23680
23921
  }
@@ -23683,23 +23924,35 @@ function retiredCodexRoots(binding, tolerance) {
23683
23924
  function captureRetiredCodexDiscovery(binding, controls, rules) {
23684
23925
  if (binding.agent !== "codex" || binding.method !== "reviewed" || !controls.length)
23685
23926
  return binding;
23686
- const cache = dirname25(dirname25(controls[0].pluginParent));
23687
- const candidates = binding.sources.filter((source) => source.path.endsWith(sep14 + ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined && [undefined, "bytes"].includes(source.hashMode)).map((source) => dirname25(dirname25(source.path)));
23927
+ const cache = dirname26(dirname26(controls[0].pluginParent));
23928
+ const candidates = binding.sources.filter((source) => source.path.endsWith(sep15 + ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined && [undefined, "bytes"].includes(source.hashMode)).map((source) => dirname26(dirname26(source.path)));
23688
23929
  const roots = [...new Set(candidates)].filter((root2) => !codexDirectHookSelects(cache, root2) && reviewedRetiredCodexRoot(cache, root2, controls, rules, (path) => read2(path), binding.codexRetiredMaterializations?.roots.includes(root2) ?? false)).sort();
23689
23930
  const parents3 = reviewedCodexPluginCapabilitiesUnchanged(cache, controls, (path) => read2(path), rules) ? [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && reviewedDisabledCodexPluginParent(cache, parent, controls, rules)).sort() : [];
23690
23931
  const { codexRetiredMaterializations: previous, ...current } = binding;
23691
23932
  if (!roots.length && !parents3.length)
23692
23933
  return current;
23693
23934
  const installation = codexInstallationRoots(binding);
23694
- const directories = (binding.directories ?? []).filter((directory2) => !installation.some((root2) => directory2.path === root2 || directory2.path.startsWith(root2 + sep14) || root2.startsWith(directory2.path + sep14)));
23935
+ const directories = (binding.directories ?? []).filter((directory2) => !installation.some((root2) => directory2.path === root2 || directory2.path.startsWith(root2 + sep15) || root2.startsWith(directory2.path + sep15)));
23695
23936
  if (previous && isDeepStrictEqual9(previous.roots, roots) && isDeepStrictEqual9(previous.parents, parents3) && isDeepStrictEqual9(previous.directories.map((item) => item.path), directories.map((item) => item.path)))
23696
23937
  return { ...current, codexRetiredMaterializations: previous };
23697
23938
  return { ...current, codexRetiredMaterializations: { roots, parents: parents3, directories: captureDiscoveryDirectoryProjection(directories.map((item) => item.path), roots) } };
23698
23939
  }
23699
- function retainableAbsentSource(source) {
23940
+ function retainableAbsentWitness(source) {
23700
23941
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined)
23701
- return false;
23702
- return [undefined, "bytes"].includes(source.hashMode) || source.hashMode === "claude-plugin-manifest-v1" && source.sha256 !== null && source.path.endsWith(join37(sep14, ".claude-plugin", "plugin.json")) || source.hashMode === CLAUDE_ORPHAN_MARKER_MODE && source.sha256 !== null && isClaudePluginCacheOrphanMarker(source.path);
23942
+ return null;
23943
+ if ([undefined, "bytes"].includes(source.hashMode))
23944
+ return "bytes";
23945
+ if (source.hashMode === "claude-plugin-manifest-v1" && source.sha256 !== null && source.path.endsWith(join38(sep15, ".claude-plugin", "plugin.json")))
23946
+ return "claude-plugin-manifest-v1";
23947
+ if (source.hashMode === CLAUDE_ORPHAN_MARKER_MODE && source.sha256 !== null && isClaudePluginCacheOrphanMarker(source.path))
23948
+ return CLAUDE_ORPHAN_MARKER_MODE;
23949
+ return null;
23950
+ }
23951
+ function retainableAbsentSource(source) {
23952
+ return retainableAbsentWitness(source) !== null;
23953
+ }
23954
+ function retainedAbsentRoot(absentRoots, source) {
23955
+ return retainableAbsentSource(source) ? absentRoots.find((root2) => source.path.startsWith(root2 + sep15)) : undefined;
23703
23956
  }
23704
23957
  function absentRetiredClaudeRoots(binding) {
23705
23958
  if (binding.agent !== "claude" || binding.method !== "reviewed")
@@ -23708,17 +23961,17 @@ function absentRetiredClaudeRoots(binding) {
23708
23961
  for (const source of binding.sources) {
23709
23962
  if (!retainableAbsentSource(source) || source.sha256 === null)
23710
23963
  continue;
23711
- const marker = sep14 + ".claude" + sep14 + "plugins" + sep14 + "cache" + sep14, at = source.path.indexOf(marker);
23964
+ const marker = sep15 + ".claude" + sep15 + "plugins" + sep15 + "cache" + sep15, at = source.path.indexOf(marker);
23712
23965
  if (at < 0 || source.path.indexOf(marker, at + 1) >= 0)
23713
23966
  continue;
23714
- const cache = source.path.slice(0, at + marker.length - 1), parts = source.path.slice(at + marker.length).split(sep14);
23967
+ const cache = source.path.slice(0, at + marker.length - 1), parts = source.path.slice(at + marker.length).split(sep15);
23715
23968
  if (parts.length < 4 || parts.slice(0, 3).some((part) => !part || part === "." || part === ".."))
23716
23969
  continue;
23717
- const root2 = join37(cache, ...parts.slice(0, 3));
23970
+ const root2 = join38(cache, ...parts.slice(0, 3));
23718
23971
  safe6(root2);
23719
- if (lstatSync31(root2, { throwIfNoEntry: false }))
23972
+ if (lstatSync32(root2, { throwIfNoEntry: false }))
23720
23973
  continue;
23721
- const claude = dirname25(dirname25(cache)), settings = join37(claude, "settings.json"), registry = join37(claude, "plugins/installed_plugins.json");
23974
+ const claude = dirname26(dirname26(cache)), settings = join38(claude, "settings.json"), registry = join38(claude, "plugins/installed_plugins.json");
23722
23975
  const full = (path) => binding.sources.find((item) => item.path === path && item.sha256 !== null && item.format === undefined && item.fields === undefined);
23723
23976
  const configSource = full(settings), registrySource = full(registry);
23724
23977
  if (!configSource || !registrySource || projected(configSource) !== configSource.sha256 || projected(registrySource) !== registrySource.sha256)
@@ -23735,15 +23988,15 @@ function absentRetiredClaudeRoots(binding) {
23735
23988
  if (!row || typeof row !== "object" || Array.isArray(row))
23736
23989
  return null;
23737
23990
  const value = row;
23738
- if (typeof value.installPath !== "string" || /[\x00-\x1f\x7f]/.test(value.installPath) || !isAbsolute28(value.installPath) || resolve26(value.installPath) !== value.installPath || value.installPath.length > AGENT_POLICY_LIMITS.pathCharacters)
23991
+ if (typeof value.installPath !== "string" || /[\x00-\x1f\x7f]/.test(value.installPath) || !isAbsolute28(value.installPath) || resolve27(value.installPath) !== value.installPath || value.installPath.length > AGENT_POLICY_LIMITS.pathCharacters)
23739
23992
  return null;
23740
23993
  if (value.scope === "user")
23741
23994
  return value.projectPath === undefined || value.projectPath === null ? "user\x00" : null;
23742
23995
  if (value.scope === "project")
23743
- return typeof value.projectPath === "string" && !/[\x00-\x1f\x7f]/.test(value.projectPath) && value.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute28(value.projectPath) && resolve26(value.projectPath) === value.projectPath ? `project\x00${value.projectPath}` : null;
23996
+ return typeof value.projectPath === "string" && !/[\x00-\x1f\x7f]/.test(value.projectPath) && value.projectPath.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute28(value.projectPath) && resolve27(value.projectPath) === value.projectPath ? `project\x00${value.projectPath}` : null;
23744
23997
  return null;
23745
23998
  };
23746
- if (Object.values(installed.plugins).some((value) => !Array.isArray(value) || value.some((row) => !row || typeof row.installPath !== "string" || row.installPath === root2 || row.installPath.startsWith(root2 + sep14) || root2.startsWith(row.installPath + sep14))))
23999
+ if (Object.values(installed.plugins).some((value) => !Array.isArray(value) || value.some((row) => !row || typeof row.installPath !== "string" || row.installPath === root2 || row.installPath.startsWith(root2 + sep15) || root2.startsWith(row.installPath + sep15))))
23747
24000
  continue;
23748
24001
  const rows = installed.plugins[id];
23749
24002
  if (!Array.isArray(rows) || rows.length < 1)
@@ -23761,9 +24014,9 @@ function absentRetiredClaudeRoots(binding) {
23761
24014
  if (!targetValid || !targetScopes.has("user\x00"))
23762
24015
  continue;
23763
24016
  const active = rows[0]?.installPath;
23764
- if (typeof active !== "string" || rows[0]?.version !== basename15(active) || dirname25(active) !== dirname25(root2) || active === root2 || !lstatSync31(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync13(active) !== active)
24017
+ if (typeof active !== "string" || rows[0]?.version !== basename15(active) || dirname26(active) !== dirname26(root2) || active === root2 || !lstatSync32(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync13(active) !== active)
23765
24018
  continue;
23766
- const activeManifest = full(join37(active, ".claude-plugin/plugin.json"));
24019
+ const activeManifest = full(join38(active, ".claude-plugin/plugin.json"));
23767
24020
  if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
23768
24021
  continue;
23769
24022
  result.add(root2);
@@ -23771,24 +24024,24 @@ function absentRetiredClaudeRoots(binding) {
23771
24024
  return [...result];
23772
24025
  }
23773
24026
  function codexSemanticCacheSourceRole(source, cacheRoot, parents3, hookParents = []) {
23774
- if (!isAbsolute28(source.path) || resolve26(source.path) !== source.path || /[\x00-\x1f\x7f]/.test(source.path) || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.reviewArtifact !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes")
24027
+ if (!isAbsolute28(source.path) || resolve27(source.path) !== source.path || /[\x00-\x1f\x7f]/.test(source.path) || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.reviewArtifact !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes")
23775
24028
  return false;
23776
- const parts = relative10(cacheRoot, source.path).split(sep14);
23777
- if (parts.length < 3 || !parents3.includes(join37(cacheRoot, parts[0], parts[1])))
24029
+ const parts = relative10(cacheRoot, source.path).split(sep15);
24030
+ if (parts.length < 3 || !parents3.includes(join38(cacheRoot, parts[0], parts[1])))
23778
24031
  return false;
23779
24032
  const tail = parts.slice(3).join("/");
23780
- const parent = join37(cacheRoot, parts[0], parts[1]);
24033
+ const parent = join38(cacheRoot, parts[0], parts[1]);
23781
24034
  return parts.length === 5 && tail === ".codex-plugin/plugin.json" && source.sha256 !== null || parts.length === 4 && [".app.json", ".mcp.json"].includes(tail) && source.sha256 !== null || parts.length === 5 && tail === "hooks/hooks.json" && hookParents.includes(parent) && source.sha256 !== null || source.sha256 === null && (parts.length === 4 && tail === "SKILL.md" || parts.length === 5 && tail === "hooks/hooks.json");
23782
24035
  }
23783
24036
  function codexSemanticCacheDirectoryRole(path, cacheRoot) {
23784
- return isAbsolute28(path) && resolve26(path) === path && !/[\x00-\x1f\x7f]/.test(path) && (path === cacheRoot || path.startsWith(cacheRoot + sep14));
24037
+ return isAbsolute28(path) && resolve27(path) === path && !/[\x00-\x1f\x7f]/.test(path) && (path === cacheRoot || path.startsWith(cacheRoot + sep15));
23785
24038
  }
23786
24039
  function assertCodexSemanticCacheHookSafety(cacheRoot, parents3, configPath, hookRootAlias = null) {
23787
24040
  const selectionRoots = [cacheRoot, ...parents3];
23788
24041
  if (hookRootAlias !== null) {
23789
- if (!hookRootAlias || Object.keys(hookRootAlias).length !== 2 || [hookRootAlias.alias, hookRootAlias.target].some((path) => typeof path !== "string" || !isAbsolute28(path) || resolve26(path) !== path || /[\x00-\x1f\x7f]/.test(path)) || hookRootAlias.target !== dirname25(configPath) || cacheRoot !== join37(hookRootAlias.target, "plugins/cache") || !lstatSync31(hookRootAlias.alias, { throwIfNoEntry: false })?.isSymbolicLink() || realpathSync13(hookRootAlias.alias) !== hookRootAlias.target || realpathSync13(hookRootAlias.target) !== hookRootAlias.target)
24042
+ if (!hookRootAlias || Object.keys(hookRootAlias).length !== 2 || [hookRootAlias.alias, hookRootAlias.target].some((path) => typeof path !== "string" || !isAbsolute28(path) || resolve27(path) !== path || /[\x00-\x1f\x7f]/.test(path)) || hookRootAlias.target !== dirname26(configPath) || cacheRoot !== join38(hookRootAlias.target, "plugins/cache") || !lstatSync32(hookRootAlias.alias, { throwIfNoEntry: false })?.isSymbolicLink() || realpathSync13(hookRootAlias.alias) !== hookRootAlias.target || realpathSync13(hookRootAlias.target) !== hookRootAlias.target)
23790
24043
  throw new Error("Reviewed semantic cache hook alias changed");
23791
- selectionRoots.push(...selectionRoots.map((root2) => join37(hookRootAlias.alias, relative10(hookRootAlias.target, root2))));
24044
+ selectionRoots.push(...selectionRoots.map((root2) => join38(hookRootAlias.alias, relative10(hookRootAlias.target, root2))));
23792
24045
  }
23793
24046
  selectionRoots.push("plugins/cache");
23794
24047
  if (selectionRoots.some((parent) => codexDirectHookSelects(cacheRoot, parent, configPath)))
@@ -23808,7 +24061,7 @@ function verifyCodexSemanticCacheDiscovery(binding, reviewedRootAliases) {
23808
24061
  if (binding.agent !== "codex" || binding.method !== "reviewed" || !controls?.length || proof.schema !== "hasna.codex-semantic-cache-discovery.v1" || !/^[a-f0-9]{64}$/.test(proof.previousPolicySha256) || !Array.isArray(proof.appOnlyParents) || !Array.isArray(proof.supersededSources) || !Array.isArray(proof.supersededDirectories))
23809
24062
  throw new Error("Invalid Codex semantic cache discovery proof");
23810
24063
  const cacheRoot = proof.witness.cacheRoot;
23811
- if (controls.some((control) => dirname25(dirname25(control.pluginParent)) !== cacheRoot))
24064
+ if (controls.some((control) => dirname26(dirname26(control.pluginParent)) !== cacheRoot))
23812
24065
  throw new Error("Codex semantic cache names another enrolled cache");
23813
24066
  const reviewedHookParents = proof.reviewedHookParents ?? [];
23814
24067
  if (!Array.isArray(reviewedHookParents) || reviewedHookParents.length > 4096 || reviewedHookParents.some((parent) => !parent || !(parent.installation === "remote-receipt" && Object.keys(parent).sort().join(",") === "installation,namespace,pluginId,pluginParent,remotePluginId,role" && typeof parent.remotePluginId === "string" || parent.installation === "local-marketplace" && Object.keys(parent).sort().join(",") === "installation,marketplaceSha256,marketplaceSourcePath,namespace,pluginId,pluginParent,role" && typeof parent.marketplaceSourcePath === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256 ?? "")) || parent.role !== "reviewed-hook" || typeof parent.pluginId !== "string" || typeof parent.namespace !== "string" || typeof parent.pluginParent !== "string") || new Set(reviewedHookParents.map((parent) => parent.pluginParent)).size !== reviewedHookParents.length || !isDeepStrictEqual9((proof.witness.hookParents ?? []).map(({ manifestSha256: _manifest, hooksSha256: _hooks, receiptSha256: _receipt, ...parent }) => parent), reviewedHookParents)) {
@@ -23824,7 +24077,7 @@ function verifyCodexSemanticCacheDiscovery(binding, reviewedRootAliases) {
23824
24077
  if (!directories.length || !isDeepStrictEqual9(sources, proof.supersededSources) || !isDeepStrictEqual9(directories, proof.supersededDirectories))
23825
24078
  throw new Error("Codex semantic cache provenance changed");
23826
24079
  const configPath = proof.configPath;
23827
- if (!isAbsolute28(configPath) || resolve26(configPath) !== configPath || basename15(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
24080
+ if (!isAbsolute28(configPath) || resolve27(configPath) !== configPath || basename15(configPath) !== "config.toml" || /[\x00-\x1f\x7f]/.test(configPath))
23828
24081
  throw new Error("Invalid semantic cache configuration source");
23829
24082
  const configSource = binding.sources.find((source) => source.path === configPath && source.sha256 !== null && source.format === undefined && source.fields === undefined);
23830
24083
  if (!configSource || projected(configSource) !== configSource.sha256)
@@ -23846,10 +24099,10 @@ function verifyAgentDiscovery(binding, codexRecovery, reviewedRootAliases, witne
23846
24099
  const semanticDirectory = (path) => Boolean(semanticCache && codexSemanticCacheDirectoryRole(path, semanticCache.witness.cacheRoot));
23847
24100
  const installationRoots = codexInstallationRoots(binding);
23848
24101
  const retiredRoots = retiredCodexRoots(binding, witnessTolerance);
23849
- const installationInput = (path) => installationRoots.some((root2) => path === root2 || path.startsWith(root2 + sep14));
24102
+ const installationInput = (path) => installationRoots.some((root2) => path === root2 || path.startsWith(root2 + sep15));
23850
24103
  if (binding.directories !== undefined) {
23851
24104
  const projected2 = binding.codexInstallationInputs?.directories ?? [];
23852
- const ancestors2 = binding.directories.filter((directory2) => installationRoots.some((root2) => root2.startsWith(directory2.path + sep14)));
24105
+ const ancestors2 = binding.directories.filter((directory2) => installationRoots.some((root2) => root2.startsWith(directory2.path + sep15)));
23853
24106
  if (projected2.length !== ancestors2.length || projected2.some((directory2) => !ancestors2.some((original) => original.path === directory2.path)))
23854
24107
  throw new Error("Missing installation input directory projection");
23855
24108
  const ordinary = binding.directories.filter((directory2) => !installationInput(directory2.path) && !ancestors2.includes(directory2) && !semanticDirectory(directory2.path));
@@ -23857,7 +24110,7 @@ function verifyAgentDiscovery(binding, codexRecovery, reviewedRootAliases, witne
23857
24110
  const projection = binding.codexRetiredMaterializations.directories.filter((directory2) => !semanticDirectory(directory2.path));
23858
24111
  if (projection.length !== ordinary.length || projection.some((item, index) => item.path !== ordinary[index]?.path))
23859
24112
  throw new Error("Missing retired Codex directory projection");
23860
- const omitted = [...retiredRoots, ...(binding.codexRetiredMaterializations.parents ?? []).filter((parent) => !lstatSync31(parent, { throwIfNoEntry: false }))];
24113
+ const omitted = [...retiredRoots, ...(binding.codexRetiredMaterializations.parents ?? []).filter((parent) => !lstatSync32(parent, { throwIfNoEntry: false }))];
23861
24114
  if (projection.every((item) => Array.isArray(item.entries)))
23862
24115
  verifyDiscoveryDirectoryProjection(projection, omitted, witnessTolerance);
23863
24116
  else {
@@ -23874,7 +24127,7 @@ function verifyAgentDiscovery(binding, codexRecovery, reviewedRootAliases, witne
23874
24127
  verifyDiscoveryDirectories(ordinary, [], witnessTolerance);
23875
24128
  verifyDiscoveryDirectories(projected2, installationRoots, witnessTolerance);
23876
24129
  }
23877
- const absentRoots = [...absentCodexSkillRoots(binding, witnessTolerance), ...absentRetiredClaudeRoots(binding), ...retiredRoots.filter((root2) => !lstatSync31(root2, { throwIfNoEntry: false }))];
24130
+ const absentRoots = [...absentCodexSkillRoots(binding, witnessTolerance), ...absentRetiredClaudeRoots(binding), ...retiredRoots.filter((root2) => !lstatSync32(root2, { throwIfNoEntry: false }))];
23878
24131
  const budget2 = discoveryByteBudget();
23879
24132
  for (const source of binding.sources) {
23880
24133
  assertMarketplaceBinding(binding, source);
@@ -23892,7 +24145,7 @@ function verifyAgentDiscovery(binding, codexRecovery, reviewedRootAliases, witne
23892
24145
  safe6(source.path);
23893
24146
  continue;
23894
24147
  }
23895
- if (absentRoots.some((root2) => source.path.startsWith(root2 + sep14)) && retainableAbsentSource(source)) {
24148
+ if (retainedAbsentRoot(absentRoots, source) !== undefined) {
23896
24149
  safe6(source.path);
23897
24150
  continue;
23898
24151
  }
@@ -23912,23 +24165,31 @@ function verifyAgentDiscovery(binding, codexRecovery, reviewedRootAliases, witne
23912
24165
  safe6(root2);
23913
24166
  for (const root2 of absentRoots) {
23914
24167
  safe6(root2);
23915
- if (lstatSync31(root2, { throwIfNoEntry: false }))
24168
+ if (lstatSync32(root2, { throwIfNoEntry: false }))
23916
24169
  throw new Error("Retired native materialization changed during discovery verification");
23917
24170
  }
23918
24171
  }
24172
+ var CLAUDE_CONTROL_WITNESS_MODES = new Set(["claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry", "claude-marketplace-registry-v2", "claude-marketplace-entry-v1", "claude-plugin-registry"]);
24173
+ var CLAUDE_CONTROL_FILES = new Set(["settings.json", "settings.local.json", "installed_plugins.json", "known_marketplaces.json"]);
24174
+ function claudeControlSource(source) {
24175
+ return CLAUDE_CONTROL_WITNESS_MODES.has(source.hashMode ?? "") || CLAUDE_CONTROL_FILES.has(basename15(source.path)) && !source.path.includes(`${sep15}.claude${sep15}plugins${sep15}cache${sep15}`);
24176
+ }
24177
+ function retainedAbsentClaudeRoot(absentRoots, source) {
24178
+ return claudeControlSource(source) ? undefined : retainedAbsentRoot(absentRoots, source);
24179
+ }
23919
24180
  function codexInstallationRoots(binding) {
23920
24181
  const proof = binding.codexInstallationInputs;
23921
24182
  if (proof === undefined)
23922
24183
  return [];
23923
24184
  if (binding.agent !== "codex" || binding.method !== "reviewed" || !supportsCodexNativeCapability(proof.version, "installed-plugin-review") || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
23924
24185
  throw new Error("Invalid native installation input proof");
23925
- const cache = dirname25(dirname25(proof.plugins[0].pluginParent));
23926
- const config = join37(dirname25(dirname25(cache)), "config.toml"), home = dirname25(dirname25(dirname25(cache)));
24186
+ const cache = dirname26(dirname26(proof.plugins[0].pluginParent));
24187
+ const config = join38(dirname26(dirname26(cache)), "config.toml"), home = dirname26(dirname26(dirname26(cache)));
23927
24188
  const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
23928
- const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join37(home, path));
23929
- if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep14) || input.sourceRoot.startsWith(root2 + sep14)) || binding.sources.some((source) => basename15(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep14)))))
24189
+ const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join38(home, path));
24190
+ if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep15) || input.sourceRoot.startsWith(root2 + sep15)) || binding.sources.some((source) => basename15(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep15)))))
23930
24191
  throw new Error("Native installation input overlaps configuration or loading roots");
23931
- if (proof.plugins.some((input) => dirname25(dirname25(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join37(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
24192
+ if (proof.plugins.some((input) => dirname26(dirname26(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join38(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
23932
24193
  throw new Error("Missing native installation input source witness");
23933
24194
  return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
23934
24195
  const text2 = read2(path);
@@ -23944,7 +24205,7 @@ function assertMarketplaceBinding(binding, source) {
23944
24205
  throw new Error("Provider executable witnesses require explicit reviewed Codex V6");
23945
24206
  if (source.hashMode === "codex-settings-v6" && (new Set(binding.sources.map((row) => row.path)).size !== binding.sources.length || !Array.isArray(source.hookSources) || source.hookSources.some((hook) => !binding.sources.some((row) => row.path === hook.path && row.sha256 === hook.sha256 && [undefined, "bytes"].includes(row.hashMode) && row.format === undefined && row.fields === undefined))))
23946
24207
  throw new Error("Missing independently reviewed Codex hook source population");
23947
- if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename15(source.path) !== "plugin.json" || !source.path.endsWith(join37(sep14, ".claude-plugin", "plugin.json"))))
24208
+ if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename15(source.path) !== "plugin.json" || !source.path.endsWith(join38(sep15, ".claude-plugin", "plugin.json"))))
23948
24209
  throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
23949
24210
  if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename15(source.path) !== "sumi.json"))
23950
24211
  throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
@@ -23957,10 +24218,11 @@ function assertMarketplaceBinding(binding, source) {
23957
24218
  if (source.hashMode === "claude-marketplace-entry-v1" && (binding.agent !== "claude" || binding.method !== "reviewed" || !claudeMarketplaceEntrySourceValid(source)))
23958
24219
  throw new Error("Claude marketplace entry witnesses require explicit reviewed Claude discovery of one named entry");
23959
24220
  }
24221
+ var ADOPTABLE_SETTINGS_MATCHES = Object.freeze(["exact", "screen-reader-receipt"]);
23960
24222
  function rebindAgentDiscovery(binding, changes) {
23961
24223
  const semanticCache = verifyCodexSemanticCacheDiscovery(binding);
23962
24224
  const budget2 = discoveryByteBudget();
23963
- const installationRoots = codexInstallationRoots(binding), absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredCodexRoots(binding).filter((root2) => !lstatSync31(root2, { throwIfNoEntry: false }))];
24225
+ const installationRoots = codexInstallationRoots(binding), absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredCodexRoots(binding).filter((root2) => !lstatSync32(root2, { throwIfNoEntry: false }))];
23964
24226
  const rebound = { ...binding, sources: binding.sources.map((source) => {
23965
24227
  assertMarketplaceBinding(binding, source);
23966
24228
  if (semanticCache?.supersededSources.some((previous) => isDeepStrictEqual9(previous, source))) {
@@ -23968,26 +24230,28 @@ function rebindAgentDiscovery(binding, changes) {
23968
24230
  throw new Error("Codex semantic cache sources cannot be written by hook rendering");
23969
24231
  return source;
23970
24232
  }
23971
- if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep14)) || absentRoots.some((root2) => source.path.startsWith(root2 + sep14)) && retainableAbsentSource(source)))
24233
+ if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep15)) || retainedAbsentRoot(absentRoots, source) !== undefined))
23972
24234
  return source;
23973
24235
  const sha2563 = projected(source, changes, budget2);
23974
24236
  if (!changes.has(source.path) && !(source.hashMode === "codex-settings-v6" && source.hookSources.some((hook) => changes.has(hook.path))) && sha2563 !== source.sha256)
23975
24237
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
24238
+ if (changes.has(source.path) && (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5") && !ADOPTABLE_SETTINGS_MATCHES.includes(codexSettingsWitnessMatch(source.path, source.hashMode, source.sha256, budget2)))
24239
+ throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
23976
24240
  return source.hashMode === "codex-settings-v6" ? { ...source, ...captureCodexSettingsV6(source.path, source.hookSources, budget2, changes) } : { ...source, sha256: sha2563 };
23977
24241
  }) };
23978
24242
  for (const root2 of absentRoots) {
23979
24243
  safe6(root2);
23980
- if (lstatSync31(root2, { throwIfNoEntry: false }))
24244
+ if (lstatSync32(root2, { throwIfNoEntry: false }))
23981
24245
  throw new Error("Retired native materialization changed during discovery rebinding");
23982
24246
  }
23983
24247
  return rebound;
23984
24248
  }
23985
24249
  function hashReviewedExecutable(path, budget2) {
23986
- const before = lstatSync31(path, { bigint: true });
24250
+ const before = lstatSync32(path, { bigint: true });
23987
24251
  if (before.size < 0n || before.size > BigInt(budget2.remaining))
23988
24252
  throw new Error("Discovery aggregate executable byte limit exceeded");
23989
24253
  const digest3 = pluginExecutableDigest(path).slice(7);
23990
- const after = lstatSync31(path, { bigint: true });
24254
+ const after = lstatSync32(path, { bigint: true });
23991
24255
  for (const key2 of ["dev", "ino", "size", "mode", "uid", "gid", "nlink", "mtimeNs", "ctimeNs"]) {
23992
24256
  if (before[key2] !== after[key2])
23993
24257
  throw new Error("Discovery executable changed during budgeted capture");
@@ -24013,12 +24277,13 @@ function captureDiscoveryPathSources(paths) {
24013
24277
  const budget2 = discoveryByteBudget();
24014
24278
  return paths.map((path) => isClaudePluginCacheOrphanMarker(path) ? { path, hashMode: CLAUDE_ORPHAN_MARKER_MODE, sha256: hashClaudePluginCacheOrphanMarker(path, budget2) } : { path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget2) });
24015
24279
  }
24016
- function assertProjectDiscovery(agent, directories, home, canonical9 = resolve26, reviewed) {
24280
+ var CLAUDE_PROJECT_DISCOVERY_KEYS = Object.freeze(["enabledPlugins", "extraKnownMarketplaces", "additionalMarketplaces", "skillOverrides"]);
24281
+ function assertProjectDiscovery(agent, directories, home, canonical9 = resolve27, reviewed) {
24017
24282
  const homeConfigPath = canonical9(agentDiscoveryConfigPath(home, agent));
24018
24283
  const v6 = agent === "codex" && reviewed?.agent === "codex" && reviewed.method === "reviewed" ? reviewed.sources.find((source) => source.hashMode === "codex-settings-v6" && source.path === homeConfigPath) : undefined;
24019
24284
  const referenced = v6 ? new Set(captureCodexHookMcpDependencies(v6.path, v6.hookSources).servers) : undefined;
24020
24285
  for (const directory2 of directories) {
24021
- const isHome = resolve26(directory2) === resolve26(home);
24286
+ const isHome = resolve27(directory2) === resolve27(home);
24022
24287
  if (agent === "hermes") {
24023
24288
  assertHermesEnvironment(home);
24024
24289
  continue;
@@ -24027,7 +24292,7 @@ function assertProjectDiscovery(agent, directories, home, canonical9 = resolve26
24027
24292
  continue;
24028
24293
  const paths = agent === "claude" ? [...isHome ? [] : [".claude/settings.json"], ".claude/settings.local.json"] : agent === "codex" ? [".codex/config.toml"] : agent === "gemini" ? [".gemini/settings.json"] : agent === "opencode" ? ["opencode.json", "opencode.jsonc", ".opencode/opencode.json", ".opencode/opencode.jsonc"] : agent === "sumi" ? ["sumi.json", "sumi.jsonc", ".sumi/sumi.json", ".sumi/sumi.jsonc"] : [".cursor/hooks.json"];
24029
24294
  for (const suffix of paths) {
24030
- const path = canonical9(join37(directory2, suffix)), raw = read2(path);
24295
+ const path = canonical9(join38(directory2, suffix)), raw = read2(path);
24031
24296
  if (raw === null)
24032
24297
  continue;
24033
24298
  const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
@@ -24042,7 +24307,7 @@ function assertProjectDiscovery(agent, directories, home, canonical9 = resolve26
24042
24307
  if (declaredHooks || providerOverride)
24043
24308
  throw new Error(`NATIVE_SKILL_DRIFT: higher-precedence project hook dependency requires review: ${path}`);
24044
24309
  }
24045
- const keys2 = agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces", "skillOverrides"] : agent === "codex" ? ["plugins", "marketplaces", "skills"] : agent === "gemini" ? ["skills", "extensions"] : agent === "opencode" ? ["plugin", "skills"] : agent === "sumi" ? ["plugin", "plugins", "skills", "permissions"] : ["hooks"];
24310
+ const keys2 = agent === "claude" ? CLAUDE_PROJECT_DISCOVERY_KEYS : agent === "codex" ? ["plugins", "marketplaces", "skills"] : agent === "gemini" ? ["skills", "extensions"] : agent === "opencode" ? ["plugin", "skills"] : agent === "sumi" ? ["plugin", "plugins", "skills", "permissions"] : ["hooks"];
24046
24311
  let admittedPlugins = false;
24047
24312
  if (agent === "claude" && reviewed?.agent === agent && reviewed.method === "reviewed" && config.enabledPlugins && typeof config.enabledPlugins === "object" && !Array.isArray(config.enabledPlugins)) {
24048
24313
  const exact = reviewed.sources.find((source) => source.path === path && source.format === undefined && source.fields === undefined && (source.hashMode === undefined || source.hashMode === "bytes"));
@@ -24056,8 +24321,8 @@ function assertProjectDiscovery(agent, directories, home, canonical9 = resolve26
24056
24321
  throw new Error(`NATIVE_SKILL_DRIFT: higher-precedence project skill or hook configuration requires review: ${path}`);
24057
24322
  }
24058
24323
  if (referenced) {
24059
- const path = canonical9(join37(directory2, ".codex/hooks.json"));
24060
- if (path !== canonical9(join37(dirname25(homeConfigPath), "hooks.json"))) {
24324
+ const path = canonical9(join38(directory2, ".codex/hooks.json"));
24325
+ if (path !== canonical9(join38(dirname26(homeConfigPath), "hooks.json"))) {
24061
24326
  const raw = read2(path);
24062
24327
  if (raw !== null) {
24063
24328
  const hooks = parseConfig(raw, path, false);
@@ -24069,15 +24334,15 @@ function assertProjectDiscovery(agent, directories, home, canonical9 = resolve26
24069
24334
  }
24070
24335
  if (agent === "sumi")
24071
24336
  for (const name of ["plugin", "plugins"]) {
24072
- const path = canonical9(join37(directory2, ".sumi", name));
24337
+ const path = canonical9(join38(directory2, ".sumi", name));
24073
24338
  safe6(path);
24074
- if (lstatSync31(path, { throwIfNoEntry: false })?.isDirectory() && readdirSync12(path).length)
24339
+ if (lstatSync32(path, { throwIfNoEntry: false })?.isDirectory() && readdirSync12(path).length)
24075
24340
  throw new Error(`NATIVE_SKILL_DRIFT: project Sumi plugins require a dedicated discovery review: ${path}`);
24076
24341
  }
24077
24342
  if (agent === "claude") {
24078
- const commands = canonical9(join37(directory2, ".claude/commands"));
24343
+ const commands = canonical9(join38(directory2, ".claude/commands"));
24079
24344
  safe6(commands);
24080
- if (lstatSync31(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync12(commands).length)
24345
+ if (lstatSync32(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync12(commands).length)
24081
24346
  throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
24082
24347
  }
24083
24348
  }
@@ -24085,7 +24350,7 @@ function assertProjectDiscovery(agent, directories, home, canonical9 = resolve26
24085
24350
  function agentDiscoveryConfigPath(home, agent) {
24086
24351
  if (agent === "sumi")
24087
24352
  return sumiConfigPath(home);
24088
- return join37(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
24353
+ return join38(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
24089
24354
  }
24090
24355
  function verifyAutomaticDiscoveryClosure(binding, options) {
24091
24356
  const command = DISCOVERY_RUNTIME_COMMANDS[binding.agent], recorded = options.executable;
@@ -24145,7 +24410,8 @@ function codexVendorConfigChangeOnly(source, cache) {
24145
24410
  if (text2 === null)
24146
24411
  return false;
24147
24412
  const rowsAffectDigest = !["codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "codex-settings-v5"].includes(source.hashMode ?? "");
24148
- return codexVendorConfigCandidates(text2, cache, { rowsAffectDigest }).some((candidate2) => replacementDigest(source, candidate2) === source.sha256);
24413
+ const typed = source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5" ? source.hashMode : undefined;
24414
+ return codexVendorConfigCandidates(text2, cache, { rowsAffectDigest }).some((candidate2) => replacementDigest(source, candidate2) === source.sha256 || typed !== undefined && codexSettingsToleratesScreenReaderReceipt(candidate2, typed, source.sha256));
24149
24415
  } catch {
24150
24416
  return false;
24151
24417
  }
@@ -24170,8 +24436,22 @@ function replacementDigest(source, text2) {
24170
24436
  return hashCodexSettingsReplacementV5(text2, budget2);
24171
24437
  return null;
24172
24438
  }
24439
+ function discoveryTextKeepsWitness(binding, path, text2) {
24440
+ if (binding.codexSemanticCache?.configPath === path)
24441
+ return false;
24442
+ return binding.sources.filter((source) => source.path === path).every((source) => {
24443
+ if (source.reviewArtifact !== undefined || source.managedPlugins !== undefined || source.hookSources !== undefined || typeof source.sha256 !== "string")
24444
+ return false;
24445
+ const typed = source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5" ? source.hashMode : undefined;
24446
+ try {
24447
+ return replacementDigest(source, text2) === source.sha256 || typed !== undefined && codexSettingsToleratesScreenReaderReceipt(text2, typed, source.sha256);
24448
+ } catch {
24449
+ return false;
24450
+ }
24451
+ });
24452
+ }
24173
24453
  function resolveAgentDiscovery(options) {
24174
- const canonical9 = options.canonical ?? resolve26, home = resolve26(options.home), agent = options.agent;
24454
+ const canonical9 = options.canonical ?? resolve27, home = resolve27(options.home), agent = options.agent;
24175
24455
  const sources = [], roots = new Set, builtinNames = [];
24176
24456
  const witness = (path, format, fields2, hashMode) => {
24177
24457
  const source = { path: canonical9(path), sha256: null, ...format ? { format, fields: fields2 } : {}, ...hashMode ? { hashMode } : {} };
@@ -24192,42 +24472,42 @@ function resolveAgentDiscovery(options) {
24192
24472
  const review = options.reviewed?.agents.find((item) => item.agent === agent);
24193
24473
  if (agent === "sumi") {
24194
24474
  const directory2 = sumiConfigDirectory(home);
24195
- if (witness(join37(directory2, "sumi.jsonc")) !== null)
24475
+ if (witness(join38(directory2, "sumi.jsonc")) !== null)
24196
24476
  unresolved("JSONC configuration requires a supported format adapter");
24197
- for (const root2 of [directory2, join37(home, ".claude"), join37(home, ".agents")])
24477
+ for (const root2 of [directory2, join38(home, ".claude"), join38(home, ".agents")])
24198
24478
  for (const name of ["skill", "skills"])
24199
- roots.add(canonical9(join37(root2, name)));
24479
+ roots.add(canonical9(join38(root2, name)));
24200
24480
  if (config.skills !== undefined && (!Array.isArray(config.skills) || config.skills.some((path) => typeof path !== "string")))
24201
24481
  unresolved("skills must be a string array");
24202
24482
  for (const value of config.skills ?? []) {
24203
24483
  if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(isAbsolute28(value) || value.startsWith("~/")))
24204
24484
  unresolved("remote or relative skill sources need a dedicated discovery adapter");
24205
- roots.add(canonical9(value.startsWith("~/") ? join37(home, value.slice(2)) : value));
24485
+ roots.add(canonical9(value.startsWith("~/") ? join38(home, value.slice(2)) : value));
24206
24486
  }
24207
24487
  for (const key2 of ["plugin", "plugins"])
24208
24488
  if (config[key2] !== undefined && !Array.isArray(config[key2]))
24209
24489
  unresolved(`${key2} must be an array`);
24210
- const plugins = [join37(directory2, "plugin"), join37(directory2, "plugins")];
24490
+ const plugins = [join38(directory2, "plugin"), join38(directory2, "plugins")];
24211
24491
  for (const path of plugins) {
24212
24492
  safe6(path);
24213
- if (lstatSync31(path, { throwIfNoEntry: false })?.isDirectory())
24493
+ if (lstatSync32(path, { throwIfNoEntry: false })?.isDirectory())
24214
24494
  for (const name of readdirSync12(path).sort()) {
24215
24495
  if (name === "skills-cli.js" && path === plugins[1])
24216
24496
  continue;
24217
- const target = join37(path, name);
24218
- if (!lstatSync31(target).isFile())
24497
+ const target = join38(path, name);
24498
+ if (!lstatSync32(target).isFile())
24219
24499
  unresolved("local plugin packages need a dedicated discovery adapter");
24220
24500
  witness(target);
24221
24501
  }
24222
24502
  }
24223
24503
  for (const root2 of roots) {
24224
24504
  safe6(root2);
24225
- if (lstatSync31(root2, { throwIfNoEntry: false })?.isDirectory() && readdirSync12(root2).some((name) => name.endsWith(".md")))
24505
+ if (lstatSync32(root2, { throwIfNoEntry: false })?.isDirectory() && readdirSync12(root2).some((name) => name.endsWith(".md")))
24226
24506
  unresolved("flat markdown skills require a dedicated native migration adapter");
24227
24507
  }
24228
24508
  }
24229
24509
  if (agent === "hermes") {
24230
- const profile = witness(join37(home, ".hermes/active_profile"));
24510
+ const profile = witness(join38(home, ".hermes/active_profile"));
24231
24511
  if (profile?.trim() && profile.trim() !== "default")
24232
24512
  unresolved("a non-default profile is active");
24233
24513
  const declared = config.skills?.external_dirs ?? [], paths = typeof declared === "string" ? [declared] : declared;
@@ -24241,7 +24521,7 @@ function resolveAgentDiscovery(options) {
24241
24521
  continue;
24242
24522
  if (normalized2.startsWith("~") && normalized2 !== "~" && !normalized2.startsWith("~/"))
24243
24523
  unresolved("user-specific tilde expansion requires a separate discovery adapter");
24244
- const path = normalized2 === "~" ? home : normalized2.startsWith("~/") ? join37(home, normalized2.slice(2)) : resolve26(home, ".hermes", normalized2);
24524
+ const path = normalized2 === "~" ? home : normalized2.startsWith("~/") ? join38(home, normalized2.slice(2)) : resolve27(home, ".hermes", normalized2);
24245
24525
  safe6(path);
24246
24526
  roots.add(path);
24247
24527
  }
@@ -24250,24 +24530,24 @@ function resolveAgentDiscovery(options) {
24250
24530
  const executable2 = options.executable === undefined ? discoveryExecutable(agent) : options.executable;
24251
24531
  if (executable2) {
24252
24532
  let packageRoot;
24253
- for (let directory2 = dirname25(executable2.target), depth = 0;depth < 8; directory2 = dirname25(directory2), depth++) {
24254
- const path = join37(directory2, "package.json"), raw = read2(path);
24533
+ for (let directory2 = dirname26(executable2.target), depth = 0;depth < 8; directory2 = dirname26(directory2), depth++) {
24534
+ const path = join38(directory2, "package.json"), raw = read2(path);
24255
24535
  if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
24256
24536
  packageRoot = directory2;
24257
24537
  witness(path);
24258
24538
  break;
24259
24539
  }
24260
- if (dirname25(directory2) === directory2)
24540
+ if (dirname26(directory2) === directory2)
24261
24541
  break;
24262
24542
  }
24263
24543
  if (!packageRoot)
24264
24544
  unresolved("installed Gemini package layout is not recognized");
24265
- const builtinRoot = join37(packageRoot, "bundle/builtin");
24545
+ const builtinRoot = join38(packageRoot, "bundle/builtin");
24266
24546
  safe6(builtinRoot);
24267
- if (!lstatSync31(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
24547
+ if (!lstatSync32(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
24268
24548
  unresolved("installed Gemini builtin root is missing");
24269
24549
  for (const name of readdirSync12(builtinRoot).sort()) {
24270
- const text2 = witness(join37(builtinRoot, name, "SKILL.md"));
24550
+ const text2 = witness(join38(builtinRoot, name, "SKILL.md"));
24271
24551
  const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
24272
24552
  if (!match?.[1] || builtinNames.includes(match[1]))
24273
24553
  unresolved("installed Gemini builtin name is missing or ambiguous");
@@ -24306,14 +24586,17 @@ function resolveAgentDiscovery(options) {
24306
24586
  if (!review.sources.some((source) => source.path === canonical9(configPath)))
24307
24587
  throw new Error("Discovery review must include the agent configuration source");
24308
24588
  const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical9(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical9(configPath), source)) : sources;
24589
+ const absentRoots = absentRetiredClaudeRoots(supplied);
24309
24590
  const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
24591
+ if (retainedAbsentClaudeRoot(absentRoots, source) !== undefined)
24592
+ return source;
24310
24593
  if (agent === "claude" && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && source.reviewArtifact === undefined && [undefined, "bytes", "path-bytes"].includes(source.hashMode) && isClaudePluginCacheOrphanMarker(source.path)) {
24311
24594
  return { path: source.path, hashMode: CLAUDE_ORPHAN_MARKER_MODE, sha256: hashClaudePluginCacheOrphanMarker(source.path) };
24312
24595
  }
24313
- if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename15(source.path) !== "plugin.json" || !source.path.endsWith(join37(sep14, ".claude-plugin", "plugin.json")))
24596
+ if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename15(source.path) !== "plugin.json" || !source.path.endsWith(join38(sep15, ".claude-plugin", "plugin.json")))
24314
24597
  return source;
24315
- const pluginRoot = dirname25(dirname25(source.path)), hooksPath = join37(pluginRoot, "hooks/hooks.json");
24316
- const hookStat = lstatSync31(hooksPath, { throwIfNoEntry: false });
24598
+ const pluginRoot = dirname26(dirname26(source.path)), hooksPath = join38(pluginRoot, "hooks/hooks.json");
24599
+ const hookStat = lstatSync32(hooksPath, { throwIfNoEntry: false });
24317
24600
  const text2 = read2(source.path);
24318
24601
  if (text2 === null)
24319
24602
  throw new Error("Reviewed Claude plugin manifest is missing");
@@ -24327,8 +24610,8 @@ function resolveAgentDiscovery(options) {
24327
24610
  if (manifest.hooks !== undefined) {
24328
24611
  if (typeof manifest.hooks !== "string")
24329
24612
  throw new Error("Reviewed Claude plugin manifest has an unsupported hooks target");
24330
- const declaredHookPath = resolve26(pluginRoot, manifest.hooks);
24331
- if (!declaredHookPath.startsWith(pluginRoot + sep14))
24613
+ const declaredHookPath = resolve27(pluginRoot, manifest.hooks);
24614
+ if (!declaredHookPath.startsWith(pluginRoot + sep15))
24332
24615
  throw new Error("Reviewed Claude plugin hooks target escapes its root");
24333
24616
  reviewedHookPaths.add(declaredHookPath);
24334
24617
  }
@@ -24339,6 +24622,11 @@ function resolveAgentDiscovery(options) {
24339
24622
  });
24340
24623
  if (agent === "claude")
24341
24624
  assertReviewedClaudeCacheClosures(home, config, review, canonical9);
24625
+ for (const root2 of absentRoots) {
24626
+ safe6(root2);
24627
+ if (lstatSync32(root2, { throwIfNoEntry: false }))
24628
+ throw new Error("Retired native materialization changed during discovery review");
24629
+ }
24342
24630
  const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
24343
24631
  const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual9(candidate2, source)) === index);
24344
24632
  return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
@@ -24346,39 +24634,39 @@ function resolveAgentDiscovery(options) {
24346
24634
  function plugin(root2) {
24347
24635
  root2 = canonical9(root2);
24348
24636
  safe6(root2);
24349
- const manifestPath = join37(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
24637
+ const manifestPath = join38(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
24350
24638
  const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
24351
24639
  if (raw === null)
24352
24640
  unresolved("plugin manifest missing");
24353
24641
  const manifest = parseConfig(raw, manifestPath);
24354
- const hooks = witness(join37(root2, "hooks/hooks.json"));
24642
+ const hooks = witness(join38(root2, "hooks/hooks.json"));
24355
24643
  if (manifest.hooks !== undefined || hooks !== null)
24356
24644
  unresolved("plugin hooks require a separate no-skill-injection review");
24357
- const commands = join37(root2, "commands");
24645
+ const commands = join38(root2, "commands");
24358
24646
  safe6(commands);
24359
- if (agent === "claude" && (manifest.commands !== undefined || lstatSync31(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync12(commands).length))
24647
+ if (agent === "claude" && (manifest.commands !== undefined || lstatSync32(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync12(commands).length))
24360
24648
  unresolved("legacy command discovery requires retirement or a dedicated format adapter");
24361
- const rootSkill = witness(join37(root2, "SKILL.md"));
24649
+ const rootSkill = witness(join38(root2, "SKILL.md"));
24362
24650
  if (rootSkill !== null)
24363
24651
  roots.add(root2);
24364
24652
  const declared = manifest.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
24365
24653
  if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
24366
24654
  unresolved("unsupported plugin skill paths");
24367
24655
  if (agent === "claude")
24368
- roots.add(join37(root2, "skills"));
24656
+ roots.add(join38(root2, "skills"));
24369
24657
  for (const value of declared) {
24370
- const path = resolve26(root2, value);
24371
- if (path !== root2 && !path.startsWith(root2 + sep14))
24658
+ const path = resolve27(root2, value);
24659
+ if (path !== root2 && !path.startsWith(root2 + sep15))
24372
24660
  unresolved("plugin skill path escapes its root");
24373
- roots.add(path.endsWith(`${sep14}SKILL.md`) ? dirname25(path) : path);
24661
+ roots.add(path.endsWith(`${sep15}SKILL.md`) ? dirname26(path) : path);
24374
24662
  }
24375
24663
  }
24376
24664
  if (agent === "claude") {
24377
- const registrations = witness(join37(home, ".claude/plugins/installed_plugins.json"));
24378
- const installed = registrations === null ? {} : parseConfig(registrations, join37(home, ".claude/plugins/installed_plugins.json")).plugins;
24379
- const commands = canonical9(join37(home, ".claude/commands"));
24665
+ const registrations = witness(join38(home, ".claude/plugins/installed_plugins.json"));
24666
+ const installed = registrations === null ? {} : parseConfig(registrations, join38(home, ".claude/plugins/installed_plugins.json")).plugins;
24667
+ const commands = canonical9(join38(home, ".claude/commands"));
24380
24668
  safe6(commands);
24381
- if (lstatSync31(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync12(commands).length)
24669
+ if (lstatSync32(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync12(commands).length)
24382
24670
  unresolved("legacy home command discovery requires a dedicated format adapter");
24383
24671
  for (const [id, enabled] of Object.entries(config.enabledPlugins ?? {})) {
24384
24672
  if (enabled === false)
@@ -24400,16 +24688,16 @@ function resolveAgentDiscovery(options) {
24400
24688
  const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
24401
24689
  if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute28(market.source))
24402
24690
  unresolved("enabled plugin marketplace is not an explicit local source");
24403
- const catalogText = witness(join37(market.source, ".agents/plugins/marketplace.json"));
24691
+ const catalogText = witness(join38(market.source, ".agents/plugins/marketplace.json"));
24404
24692
  if (catalogText === null)
24405
24693
  unresolved("marketplace catalog missing");
24406
- const catalog = parseConfig(catalogText, join37(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
24694
+ const catalog = parseConfig(catalogText, join38(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
24407
24695
  if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
24408
24696
  unresolved("plugin catalog source is missing or ambiguous");
24409
- plugin(resolve26(market.source, matches[0].source.path));
24697
+ plugin(resolve27(market.source, matches[0].source.path));
24410
24698
  }
24411
24699
  } else if (agent === "opencode") {
24412
- if (witness(join37(home, ".config/opencode/opencode.jsonc")) !== null || witness(join37(home, ".config/opencode/config.json")) !== null)
24700
+ if (witness(join38(home, ".config/opencode/opencode.jsonc")) !== null || witness(join38(home, ".config/opencode/config.json")) !== null)
24413
24701
  unresolved("additional config layers require review");
24414
24702
  if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
24415
24703
  unresolved("external plugin or remote skill source");
@@ -24418,17 +24706,17 @@ function resolveAgentDiscovery(options) {
24418
24706
  unresolved("relative or malformed added skill root");
24419
24707
  roots.add(path);
24420
24708
  }
24421
- const localPlugins = join37(home, ".config/opencode/plugins");
24709
+ const localPlugins = join38(home, ".config/opencode/plugins");
24422
24710
  safe6(localPlugins);
24423
- if (lstatSync31(localPlugins, { throwIfNoEntry: false }) && readdirSync12(localPlugins).some((name) => name !== "skills-cli.js"))
24711
+ if (lstatSync32(localPlugins, { throwIfNoEntry: false }) && readdirSync12(localPlugins).some((name) => name !== "skills-cli.js"))
24424
24712
  unresolved("additional local plugin hooks require review");
24425
24713
  } else if (agent === "sumi") {
24426
- if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical9(configPath) && source.path !== canonical9(join37(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
24714
+ if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical9(configPath) && source.path !== canonical9(join38(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
24427
24715
  unresolved("external or foreign local plugins require review");
24428
24716
  } else if (agent === "hermes") {
24429
- for (const directory2 of [join37(home, ".hermes/plugins"), join37(home, ".hermes/hermes-agent")]) {
24717
+ for (const directory2 of [join38(home, ".hermes/plugins"), join38(home, ".hermes/hermes-agent")]) {
24430
24718
  safe6(directory2);
24431
- if (lstatSync31(directory2, { throwIfNoEntry: false }))
24719
+ if (lstatSync32(directory2, { throwIfNoEntry: false }))
24432
24720
  unresolved("installed runtime/plugin discovery requires reviewed source bindings");
24433
24721
  }
24434
24722
  if (Bun.which("hermes") || Object.keys(config.plugins ?? {}).length)
@@ -24436,30 +24724,30 @@ function resolveAgentDiscovery(options) {
24436
24724
  } else if (agent === "gemini") {
24437
24725
  if (Object.keys(config.extensions ?? {}).length || config.skills?.paths?.length)
24438
24726
  unresolved("custom extension or skill paths");
24439
- const directory2 = join37(home, ".gemini/extensions");
24727
+ const directory2 = join38(home, ".gemini/extensions");
24440
24728
  safe6(directory2);
24441
- if (lstatSync31(directory2, { throwIfNoEntry: false }))
24729
+ if (lstatSync32(directory2, { throwIfNoEntry: false }))
24442
24730
  for (const name of readdirSync12(directory2)) {
24443
- const root2 = join37(directory2, name);
24731
+ const root2 = join38(directory2, name);
24444
24732
  safe6(root2);
24445
- if (!lstatSync31(root2)?.isDirectory())
24733
+ if (!lstatSync32(root2)?.isDirectory())
24446
24734
  continue;
24447
- const raw = witness(join37(root2, "gemini-extension.json"));
24735
+ const raw = witness(join38(root2, "gemini-extension.json"));
24448
24736
  if (raw === null)
24449
24737
  unresolved("extension registration missing");
24450
- const manifest = parseConfig(raw, join37(root2, "gemini-extension.json"));
24451
- if (manifest.hooks !== undefined || witness(join37(root2, "hooks/hooks.json")) !== null || witness(join37(root2, ".gemini-extension-install.json")) !== null)
24738
+ const manifest = parseConfig(raw, join38(root2, "gemini-extension.json"));
24739
+ if (manifest.hooks !== undefined || witness(join38(root2, "hooks/hooks.json")) !== null || witness(join38(root2, ".gemini-extension-install.json")) !== null)
24452
24740
  unresolved("extension hooks or linked registration require review");
24453
- roots.add(join37(root2, "skills"));
24741
+ roots.add(join38(root2, "skills"));
24454
24742
  }
24455
24743
  }
24456
- return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join37(home, ".hermes/plugins"), join37(home, ".hermes/hermes-agent")].map((path) => canonical9(path))) } : {} };
24744
+ return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join38(home, ".hermes/plugins"), join38(home, ".hermes/hermes-agent")].map((path) => canonical9(path))) } : {} };
24457
24745
  }
24458
24746
 
24459
24747
  // src/lib/authoring-path.ts
24460
24748
  var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
24461
24749
  function pathParts(path) {
24462
- return resolve27(path).split(sep15).filter(Boolean);
24750
+ return resolve28(path).split(sep16).filter(Boolean);
24463
24751
  }
24464
24752
  function nativeRootMatch(path) {
24465
24753
  const parts = pathParts(path);
@@ -24473,24 +24761,24 @@ function nativeRootMatch(path) {
24473
24761
  return;
24474
24762
  }
24475
24763
  function physicalPath(path) {
24476
- let cursor = resolve27(path);
24764
+ let cursor = resolve28(path);
24477
24765
  const suffix = [];
24478
- while (!lstatSync32(cursor, { throwIfNoEntry: false })) {
24766
+ while (!lstatSync33(cursor, { throwIfNoEntry: false })) {
24479
24767
  suffix.unshift(basename16(cursor));
24480
- const parent = dirname26(cursor);
24768
+ const parent = dirname27(cursor);
24481
24769
  if (parent === cursor)
24482
- return resolve27(path);
24770
+ return resolve28(path);
24483
24771
  cursor = parent;
24484
24772
  }
24485
- return resolve27(realpathSync14(cursor), ...suffix);
24773
+ return resolve28(realpathSync14(cursor), ...suffix);
24486
24774
  }
24487
24775
  function contains(root2, path) {
24488
- return path === root2 || path.startsWith(root2 + sep15);
24776
+ return path === root2 || path.startsWith(root2 + sep16);
24489
24777
  }
24490
24778
  function configuredRoots(home, explicitHome) {
24491
- const homeData = join38(home, ".hasna", "skills");
24779
+ const homeData = join39(home, ".hasna", "skills");
24492
24780
  const homePolicy = readManagedSkillPolicySnapshot(homeData);
24493
- const configuredPolicy = !explicitHome && resolve27(getDataDirReadOnly()) !== resolve27(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
24781
+ const configuredPolicy = !explicitHome && resolve28(getDataDirReadOnly()) !== resolve28(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
24494
24782
  const policy = homePolicy ?? configuredPolicy;
24495
24783
  const bridge = policy?.value.bridge;
24496
24784
  if (bridge && (typeof bridge.home !== "string" || physicalPath(bridge.home) !== physicalPath(home))) {
@@ -24519,8 +24807,8 @@ function configuredRoots(home, explicitHome) {
24519
24807
  admitBinding(binding);
24520
24808
  continue;
24521
24809
  }
24522
- const nativeHome = join38(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
24523
- if (!lstatSync32(nativeHome, { throwIfNoEntry: false }))
24810
+ const nativeHome = join39(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
24811
+ if (!lstatSync33(nativeHome, { throwIfNoEntry: false }))
24524
24812
  continue;
24525
24813
  roots.push(...resolveAgentDiscovery({ home, agent, canonical: physicalPath }).roots);
24526
24814
  }
@@ -24529,22 +24817,22 @@ function configuredRoots(home, explicitHome) {
24529
24817
  function assertPortableAuthoringPath(path, options = {}) {
24530
24818
  if (typeof path !== "string" || !path || path.includes("\x00"))
24531
24819
  throw new Error("Invalid portable authoring path");
24532
- const lexical = resolve27(path);
24820
+ const lexical = resolve28(path);
24533
24821
  const physical = physicalPath(lexical);
24534
24822
  const candidates = [lexical, physical];
24535
24823
  let native = candidates.map(nativeRootMatch).find(Boolean);
24536
- const home = resolve27(options.homeDir ?? homedir6());
24537
- const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join38(home, ...parts));
24538
- defaults.push(join38(home, ".codex/plugins/cache"), join38(home, ".claude/plugins/cache"), join38(home, ".claude/plugins/synced"), join38(home, ".gemini/extensions"));
24824
+ const home = resolve28(options.homeDir ?? homedir6());
24825
+ const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join39(home, ...parts));
24826
+ defaults.push(join39(home, ".codex/plugins/cache"), join39(home, ".claude/plugins/cache"), join39(home, ".claude/plugins/synced"), join39(home, ".gemini/extensions"));
24539
24827
  for (const root2 of defaults) {
24540
- if (candidates.some((candidate2) => contains(resolve27(root2), candidate2) || contains(physicalPath(root2), candidate2)))
24828
+ if (candidates.some((candidate2) => contains(resolve28(root2), candidate2) || contains(physicalPath(root2), candidate2)))
24541
24829
  native ??= root2;
24542
24830
  }
24543
24831
  if (native) {
24544
24832
  throw new Error(`Refusing portable Skills authoring below native discovery root '${native}': ${lexical}. Set HASNA_SKILLS_DIR to a private authoring corpus outside native roots.`);
24545
24833
  }
24546
24834
  for (const root2 of configuredRoots(home, options.homeDir !== undefined)) {
24547
- if (candidates.some((candidate2) => contains(resolve27(root2), candidate2) || contains(physicalPath(root2), candidate2))) {
24835
+ if (candidates.some((candidate2) => contains(resolve28(root2), candidate2) || contains(physicalPath(root2), candidate2))) {
24548
24836
  throw new Error(`Refusing portable Skills authoring below configured native discovery root: ${lexical}`);
24549
24837
  }
24550
24838
  }
@@ -24556,15 +24844,15 @@ function getPortableSkillsRoot(options = {}) {
24556
24844
  function resolvePortableSkillsRoot(options, migrate) {
24557
24845
  if (options.rootDir)
24558
24846
  return options.rootDir;
24559
- const appDir = options.homeDir ? join39(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
24560
- const cache = join39(appDir, SKILLS_CACHE_DIRNAME);
24847
+ const appDir = options.homeDir ? join40(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
24848
+ const cache = join40(appDir, SKILLS_CACHE_DIRNAME);
24561
24849
  if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
24562
24850
  return cache;
24563
- const installed = join39(appDir, INSTALLED_SKILLS_DIRNAME);
24851
+ const installed = join40(appDir, INSTALLED_SKILLS_DIRNAME);
24564
24852
  return installed;
24565
24853
  }
24566
24854
  function getPortableSkillPath(name, options = {}) {
24567
- return join39(getPortableSkillsRoot(options), normalizePortableSkillName(name));
24855
+ return join40(getPortableSkillsRoot(options), normalizePortableSkillName(name));
24568
24856
  }
24569
24857
  function findPortableSkill(name, options = {}) {
24570
24858
  let normalized2;
@@ -24590,7 +24878,7 @@ function listPortableSkills(options = {}) {
24590
24878
  for (const entry of readdirSync13(root2).sort()) {
24591
24879
  if (entry.startsWith("."))
24592
24880
  continue;
24593
- const path = join39(root2, entry);
24881
+ const path = join40(root2, entry);
24594
24882
  if (!safeIsDirectory(path))
24595
24883
  continue;
24596
24884
  try {
@@ -24624,9 +24912,9 @@ function isOfficialSkillName(name) {
24624
24912
  function scaffoldPortableSkill(name, options = {}) {
24625
24913
  const skillName = normalizeNewPortableSkillName(name);
24626
24914
  const readOnlyRoot = resolvePortableSkillsRoot(options, false);
24627
- assertPortableAuthoringPath(join39(readOnlyRoot, skillName), options);
24915
+ assertPortableAuthoringPath(join40(readOnlyRoot, skillName), options);
24628
24916
  const root2 = getPortableSkillsRoot(options);
24629
- const skillPath = join39(root2, skillName);
24917
+ const skillPath = join40(root2, skillName);
24630
24918
  assertPortableAuthoringPath(skillPath, options);
24631
24919
  if (existsSync14(skillPath)) {
24632
24920
  if (!options.overwrite)
@@ -24660,7 +24948,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
24660
24948
  const skipped = [];
24661
24949
  const entries = readdirSync13(absoluteSource, { withFileTypes: true }).map((entry) => entry.name).filter((entryName) => !entryName.startsWith(".")).sort();
24662
24950
  for (const entryName of entries) {
24663
- const childPath = join39(absoluteSource, entryName);
24951
+ const childPath = join40(absoluteSource, entryName);
24664
24952
  if (!safeIsDirectory(childPath))
24665
24953
  continue;
24666
24954
  if (!isSkillCandidate(childPath)) {
@@ -24689,23 +24977,23 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
24689
24977
  };
24690
24978
  }
24691
24979
  function isSkillCandidate(dir) {
24692
- return existsSync14(join39(dir, "SKILL.md")) || existsSync14(join39(dir, "skill.json")) || existsSync14(join39(dir, "package.json"));
24980
+ return existsSync14(join40(dir, "SKILL.md")) || existsSync14(join40(dir, "skill.json")) || existsSync14(join40(dir, "package.json"));
24693
24981
  }
24694
24982
  function physicalImportPath(path) {
24695
- let ancestor = resolve28(path);
24983
+ let ancestor = resolve29(path);
24696
24984
  const suffix = [];
24697
24985
  while (!existsSync14(ancestor)) {
24698
24986
  suffix.unshift(basename17(ancestor));
24699
- ancestor = dirname27(ancestor);
24987
+ ancestor = dirname28(ancestor);
24700
24988
  }
24701
- return join39(realpathSync15(ancestor), ...suffix);
24989
+ return join40(realpathSync15(ancestor), ...suffix);
24702
24990
  }
24703
24991
  function assertDisjointImportPaths(source, destination) {
24704
24992
  const sourcePath = realpathSync15(source);
24705
24993
  const destinationPath = physicalImportPath(destination);
24706
24994
  const contains2 = (parent, child2) => {
24707
24995
  const path = relative11(parent, child2);
24708
- return path === "" || !isAbsolute29(path) && path !== ".." && !path.startsWith(`..${sep16}`);
24996
+ return path === "" || !isAbsolute29(path) && path !== ".." && !path.startsWith(`..${sep17}`);
24709
24997
  };
24710
24998
  if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
24711
24999
  throw new Error("Source and destination skill directories must not overlap");
@@ -24724,11 +25012,11 @@ function portPortableSkill(sourcePath, options = {}) {
24724
25012
  const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename17(absoluteSource)}')` : ""} matches a bundled official skill.`;
24725
25013
  throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
24726
25014
  }
24727
- const readOnlyDestination = join39(resolvePortableSkillsRoot(options, false), skillName);
25015
+ const readOnlyDestination = join40(resolvePortableSkillsRoot(options, false), skillName);
24728
25016
  assertPortableAuthoringPath(readOnlyDestination, options);
24729
25017
  assertDisjointImportPaths(absoluteSource, readOnlyDestination);
24730
25018
  const root2 = getPortableSkillsRoot(options);
24731
- const destination = join39(root2, skillName);
25019
+ const destination = join40(root2, skillName);
24732
25020
  assertPortableAuthoringPath(destination, options);
24733
25021
  assertDisjointImportPaths(absoluteSource, destination);
24734
25022
  if (existsSync14(destination)) {
@@ -24736,7 +25024,7 @@ function portPortableSkill(sourcePath, options = {}) {
24736
25024
  throw new Error(`Skill '${skillName}' already exists at ${destination}`);
24737
25025
  rmSync4(destination, { recursive: true, force: true });
24738
25026
  }
24739
- mkdirSync11(dirname27(destination), { recursive: true });
25027
+ mkdirSync11(dirname28(destination), { recursive: true });
24740
25028
  copySkillDirectory(absoluteSource, destination);
24741
25029
  const base = {
24742
25030
  ...inferred,
@@ -24773,13 +25061,13 @@ function buildCorpusManifest(input, name) {
24773
25061
  function writeCorpusSkill(input, options = {}) {
24774
25062
  const name = normalizePortableSkillName(input.name);
24775
25063
  const readOnlyRoot = resolvePortableSkillsRoot(options, false);
24776
- assertPortableAuthoringPath(join39(readOnlyRoot, name), options);
25064
+ assertPortableAuthoringPath(join40(readOnlyRoot, name), options);
24777
25065
  const root2 = getPortableSkillsRoot(options);
24778
- const skillPath = join39(root2, name);
25066
+ const skillPath = join40(root2, name);
24779
25067
  assertPortableAuthoringPath(skillPath, options);
24780
25068
  const created = !existsSync14(skillPath);
24781
25069
  mkdirSync11(skillPath, { recursive: true });
24782
- writeFileSync9(join39(skillPath, "SKILL.md"), input.skillMd);
25070
+ writeFileSync9(join40(skillPath, "SKILL.md"), input.skillMd);
24783
25071
  const manifest = buildCorpusManifest(input, name);
24784
25072
  writeSkillJsonWithHash(skillPath, manifest);
24785
25073
  return { name, path: skillPath, manifest, created };
@@ -24787,22 +25075,22 @@ function writeCorpusSkill(input, options = {}) {
24787
25075
  function installCorpusSkillAtomically(input, options = {}) {
24788
25076
  const name = normalizePortableSkillName(input.name);
24789
25077
  const readOnlyRoot = resolvePortableSkillsRoot(options, false);
24790
- assertPortableAuthoringPath(join39(readOnlyRoot, name), options);
25078
+ assertPortableAuthoringPath(join40(readOnlyRoot, name), options);
24791
25079
  const root2 = getPortableSkillsRoot(options);
24792
- const target = join39(root2, name);
25080
+ const target = join40(root2, name);
24793
25081
  assertPortableAuthoringPath(target, options);
24794
25082
  const created = !existsSync14(target);
24795
25083
  mkdirSync11(root2, { recursive: true });
24796
- const staging = mkdtempSync2(join39(root2, `.pull-${name}-`));
25084
+ const staging = mkdtempSync2(join40(root2, `.pull-${name}-`));
24797
25085
  let moved = false;
24798
25086
  let backup = null;
24799
25087
  try {
24800
- writeFileSync9(join39(staging, "SKILL.md"), input.skillMd);
25088
+ writeFileSync9(join40(staging, "SKILL.md"), input.skillMd);
24801
25089
  const manifest = buildCorpusManifest(input, name);
24802
25090
  writeSkillJsonWithHash(staging, manifest);
24803
25091
  if (existsSync14(target)) {
24804
- backup = mkdtempSync2(join39(root2, `.pull-backup-${name}-`));
24805
- renameSync6(target, join39(backup, name));
25092
+ backup = mkdtempSync2(join40(root2, `.pull-backup-${name}-`));
25093
+ renameSync6(target, join40(backup, name));
24806
25094
  moved = true;
24807
25095
  }
24808
25096
  renameSync6(staging, target);
@@ -24811,9 +25099,9 @@ function installCorpusSkillAtomically(input, options = {}) {
24811
25099
  return { name, path: target, manifest, created };
24812
25100
  } catch (error) {
24813
25101
  rmSync4(staging, { recursive: true, force: true });
24814
- if (moved && backup && existsSync14(join39(backup, name))) {
25102
+ if (moved && backup && existsSync14(join40(backup, name))) {
24815
25103
  try {
24816
- renameSync6(join39(backup, name), target);
25104
+ renameSync6(join40(backup, name), target);
24817
25105
  } catch {}
24818
25106
  }
24819
25107
  throw error;
@@ -24826,8 +25114,8 @@ function validatePortableSkillDirectory(name, skillPath) {
24826
25114
  const warnings = [...base.warnings];
24827
25115
  let manifest;
24828
25116
  if (existsSync14(skillPath)) {
24829
- const skillJsonPath = join39(skillPath, "skill.json");
24830
- const skillMdPath = join39(skillPath, "SKILL.md");
25117
+ const skillJsonPath = join40(skillPath, "skill.json");
25118
+ const skillMdPath = join40(skillPath, "SKILL.md");
24831
25119
  if (!existsSync14(skillJsonPath) && !existsSync14(skillMdPath)) {
24832
25120
  add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
24833
25121
  }
@@ -24847,7 +25135,7 @@ function validatePortableSkillDirectory(name, skillPath) {
24847
25135
  add3(issues, "portable.version_missing", "Portable manifest missing version");
24848
25136
  }
24849
25137
  const contractIssues = validatePortableManifestContract(manifest, {
24850
- strict: existsSync14(join39(skillPath, "skill.json")),
25138
+ strict: existsSync14(join40(skillPath, "skill.json")),
24851
25139
  skillPath
24852
25140
  });
24853
25141
  for (const issue of contractIssues)
@@ -24887,7 +25175,7 @@ function validatePortableSkillDirectory(name, skillPath) {
24887
25175
  add3(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
24888
25176
  continue;
24889
25177
  }
24890
- const entryPath = join39(skillPath, command.entry);
25178
+ const entryPath = join40(skillPath, command.entry);
24891
25179
  if (!existsSync14(entryPath))
24892
25180
  add3(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
24893
25181
  else if (statSync13(entryPath).isDirectory())
@@ -24898,7 +25186,7 @@ function validatePortableSkillDirectory(name, skillPath) {
24898
25186
  } catch (error) {
24899
25187
  add3(issues, "portable.manifest_invalid", error.message);
24900
25188
  }
24901
- if (manifest?.kind !== "instruction" && !existsSync14(join39(skillPath, "AGENTS.md"))) {
25189
+ if (manifest?.kind !== "instruction" && !existsSync14(join40(skillPath, "AGENTS.md"))) {
24902
25190
  add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
24903
25191
  }
24904
25192
  }
@@ -24934,12 +25222,12 @@ async function runPortableSkill(name, args, options = {}) {
24934
25222
  if (!isSafeRelativePath2(command.entry)) {
24935
25223
  return { exitCode: 1, error: `Portable skill '${name}' command entry is unsafe` };
24936
25224
  }
24937
- const entryPath = join39(skill.path, command.entry);
25225
+ const entryPath = join40(skill.path, command.entry);
24938
25226
  if (!existsSync14(entryPath)) {
24939
25227
  return { exitCode: 1, error: `Entry point '${command.entry}' not found in portable skill '${name}'` };
24940
25228
  }
24941
- const pkgPath = join39(skill.path, "package.json");
24942
- const nodeModules = join39(skill.path, "node_modules");
25229
+ const pkgPath = join40(skill.path, "package.json");
25230
+ const nodeModules = join40(skill.path, "node_modules");
24943
25231
  if (existsSync14(pkgPath) && !existsSync14(nodeModules) && hasPackageDependencies(pkgPath)) {
24944
25232
  const install = Bun.spawn(["bun", "install", "--no-save"], {
24945
25233
  cwd: skill.path,
@@ -25196,12 +25484,12 @@ function discoverSkillsInDir(dir, source = "custom") {
25196
25484
  for (const entry of entries) {
25197
25485
  if (!entry.isDirectory())
25198
25486
  continue;
25199
- const skillMdPath = join40(dir, entry.name, "SKILL.md");
25487
+ const skillMdPath = join41(dir, entry.name, "SKILL.md");
25200
25488
  if (!existsSync15(skillMdPath))
25201
25489
  continue;
25202
25490
  let content;
25203
25491
  try {
25204
- content = readFileSync26(skillMdPath, "utf-8");
25492
+ content = readFileSync27(skillMdPath, "utf-8");
25205
25493
  } catch {
25206
25494
  continue;
25207
25495
  }
@@ -25216,7 +25504,7 @@ function discoverSkillsInDir(dir, source = "custom") {
25216
25504
  category: fm.category || "Development Tools",
25217
25505
  tags: fm.tags || [],
25218
25506
  ...fm.kind ? { kind: fm.kind } : {},
25219
- ...isHostedMetadataSkillDir(join40(dir, entry.name)) ? { serverOwned: true } : {},
25507
+ ...isHostedMetadataSkillDir(join41(dir, entry.name)) ? { serverOwned: true } : {},
25220
25508
  source
25221
25509
  });
25222
25510
  }
@@ -25232,13 +25520,13 @@ function findExtensionSkillPath(name) {
25232
25520
  for (const entry of entries) {
25233
25521
  if (!entry.isDirectory())
25234
25522
  continue;
25235
- const skillDir = join40(config.extensionsDir, entry.name);
25236
- const skillMdPath = join40(skillDir, "SKILL.md");
25523
+ const skillDir = join41(config.extensionsDir, entry.name);
25524
+ const skillMdPath = join41(skillDir, "SKILL.md");
25237
25525
  if (!existsSync15(skillMdPath))
25238
25526
  continue;
25239
25527
  let content;
25240
25528
  try {
25241
- content = readFileSync26(skillMdPath, "utf-8");
25529
+ content = readFileSync27(skillMdPath, "utf-8");
25242
25530
  } catch {
25243
25531
  continue;
25244
25532
  }
@@ -25303,20 +25591,20 @@ function getAllTags(catalog) {
25303
25591
  }
25304
25592
  // src/lib/run-state.ts
25305
25593
  import { createHash as createHash31, randomBytes } from "crypto";
25306
- import { existsSync as existsSync16, mkdirSync as mkdirSync12, readFileSync as readFileSync27, readdirSync as readdirSync15, statSync as statSync14, writeFileSync as writeFileSync10 } from "fs";
25307
- import { extname, join as join41, relative as relative12 } from "path";
25594
+ import { existsSync as existsSync16, mkdirSync as mkdirSync12, readFileSync as readFileSync28, readdirSync as readdirSync15, statSync as statSync14, writeFileSync as writeFileSync10 } from "fs";
25595
+ import { extname, join as join42, relative as relative12 } from "path";
25308
25596
  function createSkillRun(params, targetDir = process.cwd()) {
25309
25597
  const now = new Date;
25310
25598
  const id = createRunId(now);
25311
25599
  const day = now.toISOString().slice(0, 10);
25312
25600
  const skillName = normalizeSkillName(params.skill);
25313
25601
  const root2 = getProjectStateDir(targetDir);
25314
- const runDir = join41(root2, "runs", day, id);
25315
- const logsDir = join41(runDir, "logs");
25316
- const exportDir = join41(root2, "exports", skillName, id);
25602
+ const runDir = join42(root2, "runs", day, id);
25603
+ const logsDir = join42(runDir, "logs");
25604
+ const exportDir = join42(root2, "exports", skillName, id);
25317
25605
  mkdirSync12(logsDir, { recursive: true });
25318
25606
  mkdirSync12(exportDir, { recursive: true });
25319
- mkdirSync12(join41(root2, "tmp"), { recursive: true });
25607
+ mkdirSync12(join42(root2, "tmp"), { recursive: true });
25320
25608
  const record5 = {
25321
25609
  id,
25322
25610
  skill: skillName,
@@ -25368,27 +25656,27 @@ function updateSkillRun(context, patch) {
25368
25656
  return context.record;
25369
25657
  }
25370
25658
  function writeRunLogs(context, stdout = "", stderr = "") {
25371
- writeFileSync10(join41(context.logsDir, "stdout.log"), stdout);
25372
- writeFileSync10(join41(context.logsDir, "stderr.log"), stderr);
25659
+ writeFileSync10(join42(context.logsDir, "stdout.log"), stdout);
25660
+ writeFileSync10(join42(context.logsDir, "stderr.log"), stderr);
25373
25661
  }
25374
25662
  function appendRunEvent(context, event, data = {}) {
25375
25663
  const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
25376
25664
  `;
25377
- const path = join41(context.runDir, "events.ndjson");
25378
- const previous = existsSync16(path) ? readFileSync27(path, "utf-8") : "";
25665
+ const path = join42(context.runDir, "events.ndjson");
25666
+ const previous = existsSync16(path) ? readFileSync28(path, "utf-8") : "";
25379
25667
  writeFileSync10(path, previous + line);
25380
25668
  }
25381
25669
  function listSkillRuns(targetDir = process.cwd(), limit = 50) {
25382
- const runsRoot = join41(getProjectStateDir(targetDir), "runs");
25670
+ const runsRoot = join42(getProjectStateDir(targetDir), "runs");
25383
25671
  if (!existsSync16(runsRoot))
25384
25672
  return [];
25385
25673
  const records = [];
25386
25674
  for (const day of readdirSync15(runsRoot).sort().reverse()) {
25387
- const dayDir = join41(runsRoot, day);
25675
+ const dayDir = join42(runsRoot, day);
25388
25676
  if (!statSync14(dayDir).isDirectory())
25389
25677
  continue;
25390
25678
  for (const runId of readdirSync15(dayDir).sort().reverse()) {
25391
- const record5 = readRunRecord(join41(dayDir, runId));
25679
+ const record5 = readRunRecord(join42(dayDir, runId));
25392
25680
  if (record5)
25393
25681
  records.push(record5);
25394
25682
  if (records.length >= limit)
@@ -25398,25 +25686,25 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
25398
25686
  return records;
25399
25687
  }
25400
25688
  function findSkillRun(runId, targetDir = process.cwd()) {
25401
- const runsRoot = join41(getProjectStateDir(targetDir), "runs");
25689
+ const runsRoot = join42(getProjectStateDir(targetDir), "runs");
25402
25690
  if (!existsSync16(runsRoot))
25403
25691
  return null;
25404
25692
  for (const day of readdirSync15(runsRoot)) {
25405
- const record5 = readRunRecord(join41(runsRoot, day, runId));
25693
+ const record5 = readRunRecord(join42(runsRoot, day, runId));
25406
25694
  if (record5)
25407
25695
  return record5;
25408
25696
  }
25409
25697
  return null;
25410
25698
  }
25411
25699
  function getRunExportDir(runId, skill, targetDir = process.cwd()) {
25412
- return join41(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
25700
+ return join42(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
25413
25701
  }
25414
25702
  function writeRunRecord(context) {
25415
- writeFileSync10(join41(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
25703
+ writeFileSync10(join42(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
25416
25704
  `);
25417
25705
  }
25418
25706
  function writeArtifactsManifest(context, artifacts) {
25419
- writeFileSync10(join41(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
25707
+ writeFileSync10(join42(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
25420
25708
  `);
25421
25709
  }
25422
25710
  function collectRunArtifacts(context) {
@@ -25425,7 +25713,7 @@ function collectRunArtifacts(context) {
25425
25713
  const artifacts = [];
25426
25714
  for (const path of walkFiles(context.exportDir)) {
25427
25715
  const stat2 = statSync14(path);
25428
- const bytes = readFileSync27(path);
25716
+ const bytes = readFileSync28(path);
25429
25717
  artifacts.push({
25430
25718
  path: toProjectRelative(context.targetDir, path),
25431
25719
  mime: mimeForPath(path),
@@ -25436,11 +25724,11 @@ function collectRunArtifacts(context) {
25436
25724
  return artifacts.sort((a, b) => a.path.localeCompare(b.path));
25437
25725
  }
25438
25726
  function readRunRecord(runDir) {
25439
- const path = join41(runDir, "run.json");
25727
+ const path = join42(runDir, "run.json");
25440
25728
  if (!existsSync16(path))
25441
25729
  return null;
25442
25730
  try {
25443
- return JSON.parse(readFileSync27(path, "utf-8"));
25731
+ return JSON.parse(readFileSync28(path, "utf-8"));
25444
25732
  } catch {
25445
25733
  return null;
25446
25734
  }
@@ -25448,7 +25736,7 @@ function readRunRecord(runDir) {
25448
25736
  function walkFiles(dir) {
25449
25737
  const files = [];
25450
25738
  for (const entry of readdirSync15(dir)) {
25451
- const full = join41(dir, entry);
25739
+ const full = join42(dir, entry);
25452
25740
  if (statSync14(full).isDirectory())
25453
25741
  files.push(...walkFiles(full));
25454
25742
  else
@@ -45853,10 +46141,10 @@ async function readQuoteUnavailableCode(response) {
45853
46141
  let timer;
45854
46142
  let deadlineExceeded = false;
45855
46143
  const expired = Symbol("quote body deadline");
45856
- const deadline = new Promise((resolve29) => {
46144
+ const deadline = new Promise((resolve30) => {
45857
46145
  timer = setTimeout(() => {
45858
46146
  deadlineExceeded = true;
45859
- resolve29(expired);
46147
+ resolve30(expired);
45860
46148
  reader.cancel().catch(() => {});
45861
46149
  }, 1500);
45862
46150
  });
@@ -46955,23 +47243,23 @@ async function createRemoteSkillsClient(env = process.env) {
46955
47243
  return connection ? new RemoteSkillsClient(connection.apiKey, connection.apiOrigin) : null;
46956
47244
  }
46957
47245
  // src/lib/scheduler.ts
46958
- import { existsSync as existsSync17, readFileSync as readFileSync28, writeFileSync as writeFileSync11, mkdirSync as mkdirSync13 } from "fs";
46959
- import { join as join42 } from "path";
47246
+ import { existsSync as existsSync17, readFileSync as readFileSync29, writeFileSync as writeFileSync11, mkdirSync as mkdirSync13 } from "fs";
47247
+ import { join as join43 } from "path";
46960
47248
  function getSchedulesPath(targetDir = process.cwd()) {
46961
- return join42(targetDir, ".skills", "schedules.json");
47249
+ return join43(targetDir, ".skills", "schedules.json");
46962
47250
  }
46963
47251
  function loadSchedules(targetDir = process.cwd()) {
46964
47252
  const path = getSchedulesPath(targetDir);
46965
47253
  if (existsSync17(path)) {
46966
47254
  try {
46967
- return JSON.parse(readFileSync28(path, "utf-8"));
47255
+ return JSON.parse(readFileSync29(path, "utf-8"));
46968
47256
  } catch {}
46969
47257
  }
46970
47258
  return { version: 1, schedules: [] };
46971
47259
  }
46972
47260
  function saveSchedules(data, targetDir = process.cwd()) {
46973
47261
  const path = getSchedulesPath(targetDir);
46974
- const dir = join42(targetDir, ".skills");
47262
+ const dir = join43(targetDir, ".skills");
46975
47263
  if (!existsSync17(dir))
46976
47264
  mkdirSync13(dir, { recursive: true });
46977
47265
  writeFileSync11(path, JSON.stringify(data, null, 2));
@@ -47187,8 +47475,8 @@ function revisionIdOfRecord(record10) {
47187
47475
  return revisionIdOf(record10);
47188
47476
  }
47189
47477
  // src/lib/pull.ts
47190
- import { existsSync as existsSync18, mkdirSync as mkdirSync14, mkdtempSync as mkdtempSync3, readFileSync as readFileSync29, renameSync as renameSync7, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "fs";
47191
- import { dirname as dirname28, join as join43 } from "path";
47478
+ import { existsSync as existsSync18, mkdirSync as mkdirSync14, mkdtempSync as mkdtempSync3, readFileSync as readFileSync30, renameSync as renameSync7, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "fs";
47479
+ import { dirname as dirname29, join as join44 } from "path";
47192
47480
 
47193
47481
  // src/lib/skill-bundles.ts
47194
47482
  import { createHmac, timingSafeEqual } from "crypto";
@@ -47282,7 +47570,7 @@ async function pullOne(client2, rawName, corpusOptions, verify) {
47282
47570
  return reconcileTombstone(slug2, corpusOptions);
47283
47571
  }
47284
47572
  if (bundleResponse.status === 404) {
47285
- const marker = readPullMarker(join43(getPortableSkillsRoot(corpusOptions), slug2));
47573
+ const marker = readPullMarker(join44(getPortableSkillsRoot(corpusOptions), slug2));
47286
47574
  if (isPublishedInstallMarker(marker)) {
47287
47575
  return { name: slug2, success: true, purged: true, removed: false };
47288
47576
  }
@@ -47321,7 +47609,7 @@ async function pullOne(client2, rawName, corpusOptions, verify) {
47321
47609
  return { name: slug2, success: false, error: `Skill '${slug2}' was not found on the configured Skills instance.` };
47322
47610
  }
47323
47611
  if (!meta3?.revisionId) {
47324
- const marker = readPullMarker(join43(getPortableSkillsRoot(corpusOptions), slug2));
47612
+ const marker = readPullMarker(join44(getPortableSkillsRoot(corpusOptions), slug2));
47325
47613
  if (isPublishedInstallMarker(marker)) {
47326
47614
  return { name: slug2, success: true, purged: true, removed: false };
47327
47615
  }
@@ -47378,7 +47666,7 @@ function provenRevision(meta3, slug2, bundle2) {
47378
47666
  return declared;
47379
47667
  }
47380
47668
  function reconcileTombstone(slug2, corpusOptions) {
47381
- const target = join43(getPortableSkillsRoot(corpusOptions), slug2);
47669
+ const target = join44(getPortableSkillsRoot(corpusOptions), slug2);
47382
47670
  if (!hasSkillsOwnershipMarker(target)) {
47383
47671
  return { name: slug2, success: true, tombstoned: true, removed: false, leftInPlace: true };
47384
47672
  }
@@ -47387,7 +47675,7 @@ function reconcileTombstone(slug2, corpusOptions) {
47387
47675
  }
47388
47676
  function readPullMarker(dir) {
47389
47677
  try {
47390
- return JSON.parse(readFileSync29(join43(dir, PULL_MARKER_FILE), "utf-8"));
47678
+ return JSON.parse(readFileSync30(join44(dir, PULL_MARKER_FILE), "utf-8"));
47391
47679
  } catch {
47392
47680
  return null;
47393
47681
  }
@@ -47522,15 +47810,15 @@ function verifyBundleResponseBytes(buffer, response, verify = {}) {
47522
47810
  function installBundleAtomically(name, entries, options = {}, marker = {}) {
47523
47811
  const root2 = getPortableSkillsRoot(options);
47524
47812
  mkdirSync14(root2, { recursive: true });
47525
- const target = join43(root2, name);
47813
+ const target = join44(root2, name);
47526
47814
  const created = !existsSync18(target);
47527
- const staging = mkdtempSync3(join43(root2, `.pull-${name}-`));
47815
+ const staging = mkdtempSync3(join44(root2, `.pull-${name}-`));
47528
47816
  let moved = false;
47529
47817
  let backup = null;
47530
47818
  try {
47531
47819
  for (const entry of entries) {
47532
- const destination = join43(staging, entry.path);
47533
- mkdirSync14(dirname28(destination), { recursive: true });
47820
+ const destination = join44(staging, entry.path);
47821
+ mkdirSync14(dirname29(destination), { recursive: true });
47534
47822
  writeFileSync12(destination, entry.bytes, { mode: entry.mode });
47535
47823
  }
47536
47824
  writePullMarker(staging, {
@@ -47542,8 +47830,8 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
47542
47830
  ...marker.revisionId ? { revisionId: marker.revisionId } : {}
47543
47831
  });
47544
47832
  if (existsSync18(target)) {
47545
- backup = mkdtempSync3(join43(root2, `.pull-backup-${name}-`));
47546
- renameSync7(target, join43(backup, name));
47833
+ backup = mkdtempSync3(join44(root2, `.pull-backup-${name}-`));
47834
+ renameSync7(target, join44(backup, name));
47547
47835
  moved = true;
47548
47836
  }
47549
47837
  renameSync7(staging, target);
@@ -47551,9 +47839,9 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
47551
47839
  rmSync5(backup, { recursive: true, force: true });
47552
47840
  } catch (error61) {
47553
47841
  rmSync5(staging, { recursive: true, force: true });
47554
- if (moved && backup && existsSync18(join43(backup, name))) {
47842
+ if (moved && backup && existsSync18(join44(backup, name))) {
47555
47843
  try {
47556
- renameSync7(join43(backup, name), target);
47844
+ renameSync7(join44(backup, name), target);
47557
47845
  } catch {}
47558
47846
  }
47559
47847
  throw error61;
@@ -47572,7 +47860,7 @@ function writePullMarker(dir, record10) {
47572
47860
  ...record10.revisionId ? { revisionId: record10.revisionId } : {},
47573
47861
  syncedAt: new Date().toISOString()
47574
47862
  };
47575
- writeFileSync12(join43(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
47863
+ writeFileSync12(join44(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
47576
47864
  `);
47577
47865
  }
47578
47866
  async function safeMeta(client2, slug2) {
@@ -48014,12 +48302,12 @@ class RemotePrivatePublicationsClient {
48014
48302
  previous = view;
48015
48303
  if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
48016
48304
  return view;
48017
- await new Promise((resolve29) => {
48305
+ await new Promise((resolve30) => {
48018
48306
  const timer = setTimeout(done, Math.min(1000, until - Date.now()));
48019
48307
  function done() {
48020
48308
  clearTimeout(timer);
48021
48309
  options.signal?.removeEventListener("abort", done);
48022
- resolve29();
48310
+ resolve30();
48023
48311
  }
48024
48312
  options.signal?.addEventListener("abort", done, { once: true });
48025
48313
  });
@@ -48434,7 +48722,7 @@ function findSkillsParityForMcpTool(tool) {
48434
48722
  }
48435
48723
  // src/lib/registry-sync.ts
48436
48724
  import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync13 } from "fs";
48437
- import { dirname as dirname29, relative as relative13 } from "path";
48725
+ import { dirname as dirname30, relative as relative13 } from "path";
48438
48726
  function createRegistrySyncArtifact(options = {}) {
48439
48727
  const profile = options.profile ?? "all";
48440
48728
  const includeDocs = options.includeDocs ?? true;
@@ -48491,7 +48779,7 @@ function createRegistrySyncArtifact(options = {}) {
48491
48779
  };
48492
48780
  }
48493
48781
  function writeRegistrySyncArtifact(path, artifact) {
48494
- mkdirSync15(dirname29(path), { recursive: true });
48782
+ mkdirSync15(dirname30(path), { recursive: true });
48495
48783
  writeFileSync13(path, `${JSON.stringify(artifact, null, 2)}
48496
48784
  `);
48497
48785
  }
@@ -49576,14 +49864,14 @@ function clone3(value) {
49576
49864
  }
49577
49865
  // src/lib/feedback.ts
49578
49866
  import { appendFileSync, existsSync as existsSync19, mkdirSync as mkdirSync16 } from "fs";
49579
- import { dirname as dirname30, join as join44 } from "path";
49867
+ import { dirname as dirname31, join as join45 } from "path";
49580
49868
  import { Database } from "bun:sqlite";
49581
49869
  function getFeedbackDbPath() {
49582
- return join44(getDataDir(), "skills.db");
49870
+ return join45(getDataDir(), "skills.db");
49583
49871
  }
49584
49872
  function getFeedbackDb() {
49585
49873
  const dbPath = getFeedbackDbPath();
49586
- const dir = dirname30(dbPath);
49874
+ const dir = dirname31(dbPath);
49587
49875
  if (!existsSync19(dir))
49588
49876
  mkdirSync16(dir, { recursive: true });
49589
49877
  const db = new Database(dbPath);
@@ -49611,8 +49899,8 @@ function saveFeedback(input2) {
49611
49899
  throw new Error("Feedback message is required");
49612
49900
  const category = input2.category ?? "general";
49613
49901
  if (isApiMode()) {
49614
- const path = join44(getDataDir(), "feedback.jsonl");
49615
- const dir = dirname30(path);
49902
+ const path = join45(getDataDir(), "feedback.jsonl");
49903
+ const dir = dirname31(path);
49616
49904
  if (!existsSync19(dir))
49617
49905
  mkdirSync16(dir, { recursive: true });
49618
49906
  appendFileSync(path, JSON.stringify({ message, category, email: input2.email ?? null, agent: input2.agent ?? null, version: input2.version ?? null, createdAt: new Date().toISOString() }) + `
@@ -49639,12 +49927,12 @@ import { createHash as createHash35, createHmac as createHmac2 } from "crypto";
49639
49927
  import {
49640
49928
  existsSync as existsSync20,
49641
49929
  mkdirSync as mkdirSync17,
49642
- readFileSync as readFileSync30,
49930
+ readFileSync as readFileSync31,
49643
49931
  readdirSync as readdirSync16,
49644
49932
  statSync as statSync15,
49645
49933
  writeFileSync as writeFileSync14
49646
49934
  } from "fs";
49647
- import { dirname as dirname31, join as join45, normalize as normalize5, relative as relative14, sep as sep17 } from "path";
49935
+ import { dirname as dirname32, join as join46, normalize as normalize5, relative as relative14, sep as sep18 } from "path";
49648
49936
  var SKILLS_STORAGE_TABLES = [
49649
49937
  "skills_sync_records",
49650
49938
  "skills_sync_cursors"
@@ -49781,7 +50069,7 @@ function getSkillsNativeStorageStatus(options = {}) {
49781
50069
  local: {
49782
50070
  dataDir: getDataDir(),
49783
50071
  projectStateDir: getProjectStateDir(targetDir),
49784
- feedbackDbPath: join45(getDataDir(), "skills.db")
50072
+ feedbackDbPath: join46(getDataDir(), "skills.db")
49785
50073
  },
49786
50074
  remote: {
49787
50075
  databaseConfigured: Boolean(config2.databaseUrl),
@@ -49806,7 +50094,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
49806
50094
  const files = [];
49807
50095
  if (existsSync20(projectStateDir)) {
49808
50096
  for (const filePath of walkFiles2(projectStateDir)) {
49809
- const bytes = readFileSync30(filePath);
50097
+ const bytes = readFileSync31(filePath);
49810
50098
  const relativePath = toPosix(relative14(targetDir, filePath));
49811
50099
  files.push({
49812
50100
  path: relativePath,
@@ -49840,7 +50128,7 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
49840
50128
  if (hash5 !== file2.sha256) {
49841
50129
  throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
49842
50130
  }
49843
- mkdirSync17(dirname31(absolutePath5), { recursive: true });
50131
+ mkdirSync17(dirname32(absolutePath5), { recursive: true });
49844
50132
  writeFileSync14(absolutePath5, bytes);
49845
50133
  written += 1;
49846
50134
  }
@@ -50138,7 +50426,7 @@ function parsePositiveInteger(value) {
50138
50426
  function walkFiles2(dir) {
50139
50427
  const files = [];
50140
50428
  for (const entry of readdirSync16(dir)) {
50141
- const full = join45(dir, entry);
50429
+ const full = join46(dir, entry);
50142
50430
  const stats = statSync15(full);
50143
50431
  if (stats.isDirectory())
50144
50432
  files.push(...walkFiles2(full));
@@ -50149,13 +50437,13 @@ function walkFiles2(dir) {
50149
50437
  }
50150
50438
  function resolveSnapshotPath(targetDir, snapshotPath) {
50151
50439
  const normalizedPath = normalize5(snapshotPath);
50152
- if (normalizedPath.startsWith("..") || normalizedPath.includes(`${sep17}..${sep17}`) || normalizedPath.startsWith(sep17)) {
50440
+ if (normalizedPath.startsWith("..") || normalizedPath.includes(`${sep18}..${sep18}`) || normalizedPath.startsWith(sep18)) {
50153
50441
  throw new Error(`Unsafe snapshot path: ${snapshotPath}`);
50154
50442
  }
50155
50443
  if (!toPosix(normalizedPath).startsWith(".skills/")) {
50156
50444
  throw new Error(`Snapshot path must stay inside .skills: ${snapshotPath}`);
50157
50445
  }
50158
- return join45(targetDir, normalizedPath);
50446
+ return join46(targetDir, normalizedPath);
50159
50447
  }
50160
50448
  function normalizeS3Prefix(prefix) {
50161
50449
  return (prefix ?? "").trim().replace(/^\/+|\/+$/g, "");
@@ -50224,16 +50512,16 @@ import { createHash as createHash36 } from "crypto";
50224
50512
  import {
50225
50513
  copyFileSync,
50226
50514
  mkdirSync as mkdirSync18,
50227
- readFileSync as readFileSync31,
50515
+ readFileSync as readFileSync32,
50228
50516
  statSync as statSync17,
50229
50517
  writeFileSync as writeFileSync15
50230
50518
  } from "fs";
50231
- import { dirname as dirname32, isAbsolute as isAbsolute31, relative as relative15, resolve as resolve29, sep as sep19 } from "path";
50519
+ import { dirname as dirname33, isAbsolute as isAbsolute31, relative as relative15, resolve as resolve30, sep as sep20 } from "path";
50232
50520
 
50233
50521
  // src/lib/portable-snapshot-filter.ts
50234
50522
  import { readdirSync as readdirSync17, statSync as statSync16 } from "fs";
50235
50523
  import { homedir as homedir7 } from "os";
50236
- import { join as join46, sep as sep18 } from "path";
50524
+ import { join as join47, sep as sep19 } from "path";
50237
50525
  var SYNC_HOMES = [
50238
50526
  { name: "skills", subClass: "skills", agent: null },
50239
50527
  { name: "custom", subClass: "custom", agent: null },
@@ -50326,16 +50614,16 @@ function isPortableWithinSkill(relativeParts) {
50326
50614
  function homePathFor(definition, homesRoot) {
50327
50615
  const home = homesRoot ?? homedir7();
50328
50616
  if (definition.subClass === "skills" || definition.subClass === "custom") {
50329
- return join46(skillsDataRootForHome(home), definition.name);
50617
+ return join47(skillsDataRootForHome(home), definition.name);
50330
50618
  }
50331
50619
  if (definition.agent === "opencode") {
50332
- return join46(home, ".config", "opencode", "skills");
50620
+ return join47(home, ".config", "opencode", "skills");
50333
50621
  }
50334
- return join46(home, `.${definition.agent}`, "skills");
50622
+ return join47(home, `.${definition.agent}`, "skills");
50335
50623
  }
50336
50624
  function destinationFor(definition, stationId, relativePath) {
50337
- const category = definition.subClass === "agent-homes" ? join46("agent-homes", definition.agent ?? "") : definition.name;
50338
- return join46("resources", stationId, "skills", category, ...relativePath.split(sep18));
50625
+ const category = definition.subClass === "agent-homes" ? join47("agent-homes", definition.agent ?? "") : definition.name;
50626
+ return join47("resources", stationId, "skills", category, ...relativePath.split(sep19));
50339
50627
  }
50340
50628
  function walkEntries(absoluteRoot) {
50341
50629
  let entries;
@@ -50346,7 +50634,7 @@ function walkEntries(absoluteRoot) {
50346
50634
  }
50347
50635
  const output2 = [];
50348
50636
  for (const entry of entries) {
50349
- const childFull = join46(absoluteRoot, entry.name);
50637
+ const childFull = join47(absoluteRoot, entry.name);
50350
50638
  if (entry.isSymbolicLink()) {
50351
50639
  output2.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
50352
50640
  continue;
@@ -50357,7 +50645,7 @@ function walkEntries(absoluteRoot) {
50357
50645
  }
50358
50646
  const nested = walkEntries(childFull);
50359
50647
  for (const item of nested) {
50360
- output2.push({ ...item, relativePath: join46(entry.name, item.relativePath) });
50648
+ output2.push({ ...item, relativePath: join47(entry.name, item.relativePath) });
50361
50649
  }
50362
50650
  continue;
50363
50651
  }
@@ -50395,7 +50683,7 @@ function validateStationId(stationId) {
50395
50683
  }
50396
50684
  }
50397
50685
  function sha256File(filePath) {
50398
- return createHash36("sha256").update(readFileSync31(filePath)).digest("hex");
50686
+ return createHash36("sha256").update(readFileSync32(filePath)).digest("hex");
50399
50687
  }
50400
50688
  function scanHome(definition, homesRoot) {
50401
50689
  const homePath = homePathFor(definition, homesRoot);
@@ -50403,7 +50691,7 @@ function scanHome(definition, homesRoot) {
50403
50691
  const portable = [];
50404
50692
  const skipped = [];
50405
50693
  for (const entry of entries) {
50406
- const relativeParts = entry.relativePath.split(sep19);
50694
+ const relativeParts = entry.relativePath.split(sep20);
50407
50695
  const fileName = relativeParts[relativeParts.length - 1];
50408
50696
  if (entry.kind === "symlink") {
50409
50697
  skipped.push({ relativePath: entry.relativePath, reason: "symlink" });
@@ -50466,7 +50754,7 @@ function humanHomes(scanned) {
50466
50754
  }));
50467
50755
  }
50468
50756
  function writeStationSnapshot(options) {
50469
- const repoRoot = resolve29(options.repoRoot ?? process.cwd());
50757
+ const repoRoot = resolve30(options.repoRoot ?? process.cwd());
50470
50758
  const { scanned, plans, totalBytes } = planStationSnapshot(options);
50471
50759
  const manifestFiles = plans.map((plan) => ({
50472
50760
  relativePath: plan.source.relativePath,
@@ -50492,9 +50780,9 @@ function writeStationSnapshot(options) {
50492
50780
  const conflicts = [];
50493
50781
  const untouched = [];
50494
50782
  for (const plan of plans) {
50495
- const destination = resolve29(repoRoot, plan.destination);
50783
+ const destination = resolve30(repoRoot, plan.destination);
50496
50784
  const destinationRelative = relative15(repoRoot, destination);
50497
- if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep19) || isAbsolute31(destinationRelative)) {
50785
+ if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep20) || isAbsolute31(destinationRelative)) {
50498
50786
  throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
50499
50787
  }
50500
50788
  let existingDigest = null;
@@ -50515,8 +50803,8 @@ function writeStationSnapshot(options) {
50515
50803
  }
50516
50804
  let written = 0;
50517
50805
  for (const plan of untouched) {
50518
- const destination = resolve29(repoRoot, plan.destination);
50519
- mkdirSync18(dirname32(destination), { recursive: true });
50806
+ const destination = resolve30(repoRoot, plan.destination);
50807
+ mkdirSync18(dirname33(destination), { recursive: true });
50520
50808
  copyFileSync(plan.source.fullPath, destination);
50521
50809
  written += 1;
50522
50810
  }
@@ -50534,8 +50822,8 @@ function writeStationSnapshot(options) {
50534
50822
  },
50535
50823
  files: manifestFiles
50536
50824
  };
50537
- const manifestPath = resolve29(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
50538
- mkdirSync18(dirname32(manifestPath), { recursive: true });
50825
+ const manifestPath = resolve30(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
50826
+ mkdirSync18(dirname33(manifestPath), { recursive: true });
50539
50827
  writeFileSync15(manifestPath, `${JSON.stringify(manifest, null, 2)}
50540
50828
  `);
50541
50829
  return {
@@ -50551,11 +50839,11 @@ import {
50551
50839
  copyFileSync as copyFileSync2,
50552
50840
  mkdirSync as mkdirSync19,
50553
50841
  readdirSync as readdirSync18,
50554
- readFileSync as readFileSync32,
50842
+ readFileSync as readFileSync33,
50555
50843
  statSync as statSync18,
50556
50844
  writeFileSync as writeFileSync16
50557
50845
  } from "fs";
50558
- import { dirname as dirname33, join as join47, resolve as resolve30, sep as sep20 } from "path";
50846
+ import { dirname as dirname34, join as join48, resolve as resolve31, sep as sep21 } from "path";
50559
50847
  var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
50560
50848
  var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
50561
50849
  var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
@@ -50563,14 +50851,14 @@ function fail2(code2, message, detail = []) {
50563
50851
  throw new StationSnapshotError(code2, message, detail);
50564
50852
  }
50565
50853
  function snapshotRootFor(repoRoot, stationId) {
50566
- return join47(repoRoot, "resources", stationId, "skills");
50854
+ return join48(repoRoot, "resources", stationId, "skills");
50567
50855
  }
50568
50856
  function readSnapshotManifest(repoRoot, stationId) {
50569
50857
  const snapshotRoot = snapshotRootFor(repoRoot, stationId);
50570
- const manifestPath = join47(snapshotRoot, "sync-manifest.json");
50858
+ const manifestPath = join48(snapshotRoot, "sync-manifest.json");
50571
50859
  let manifest;
50572
50860
  try {
50573
- manifest = JSON.parse(readFileSync32(manifestPath, "utf8"));
50861
+ manifest = JSON.parse(readFileSync33(manifestPath, "utf8"));
50574
50862
  } catch (error61) {
50575
50863
  fail2("MANIFEST_UNREADABLE", `cannot read snapshot manifest: ${manifestPath}: ${error61.message}`);
50576
50864
  }
@@ -50594,7 +50882,7 @@ function planStationHydration(stationId, repoRoot) {
50594
50882
  const hashMismatches = [];
50595
50883
  const skippedByRule = [];
50596
50884
  for (const agent of SYNC_AGENTS) {
50597
- const agentRoot = join47(snapshotRoot, "agent-homes", agent);
50885
+ const agentRoot = join48(snapshotRoot, "agent-homes", agent);
50598
50886
  let identEntries;
50599
50887
  try {
50600
50888
  identEntries = readdirSync18(agentRoot, { withFileTypes: true });
@@ -50605,10 +50893,10 @@ function planStationHydration(stationId, repoRoot) {
50605
50893
  if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
50606
50894
  continue;
50607
50895
  }
50608
- const identRoot = join47(agentRoot, identEntry.name);
50896
+ const identRoot = join48(agentRoot, identEntry.name);
50609
50897
  const entries = walkEntries(identRoot);
50610
50898
  for (const entry of entries) {
50611
- const relativeParts = [identEntry.name, ...entry.relativePath.split(sep20)];
50899
+ const relativeParts = [identEntry.name, ...entry.relativePath.split(sep21)];
50612
50900
  if (entry.kind === "symlink") {
50613
50901
  symlinks.push({ ident: identEntry.name, agent, relativePath: entry.relativePath });
50614
50902
  continue;
@@ -50632,8 +50920,8 @@ function planStationHydration(stationId, repoRoot) {
50632
50920
  });
50633
50921
  continue;
50634
50922
  }
50635
- const withinIdent = relativeParts.slice(1).join(sep20);
50636
- const homeRelative = relativeParts.join(sep20);
50923
+ const withinIdent = relativeParts.slice(1).join(sep21);
50924
+ const homeRelative = relativeParts.join(sep21);
50637
50925
  if (REFUSED_SCANNER_FLAGGED.has(homeRelative)) {
50638
50926
  skippedByRule.push({
50639
50927
  ident: identEntry.name,
@@ -50706,7 +50994,7 @@ function planStationHydration(stationId, repoRoot) {
50706
50994
  for (const copy of copies) {
50707
50995
  let isStub = false;
50708
50996
  try {
50709
- isStub = isPointerSkillMd(readFileSync32(copy.fullPath, "utf8"));
50997
+ isStub = isPointerSkillMd(readFileSync33(copy.fullPath, "utf8"));
50710
50998
  } catch {
50711
50999
  isStub = false;
50712
51000
  }
@@ -50756,8 +51044,8 @@ function skillSha256(skill) {
50756
51044
  `)).digest("hex");
50757
51045
  }
50758
51046
  function writeStationHydration(options) {
50759
- const repoRoot = resolve30(options.repoRoot ?? process.cwd());
50760
- const cacheRoot = resolve30(options.cacheRoot ?? resolveCorpusRoot());
51047
+ const repoRoot = resolve31(options.repoRoot ?? process.cwd());
51048
+ const cacheRoot = resolve31(options.cacheRoot ?? resolveCorpusRoot());
50761
51049
  const plan = planStationHydration(options.stationId, repoRoot);
50762
51050
  const resultSkills = plan.winners.map((skill) => ({
50763
51051
  ident: skill.ident,
@@ -50790,7 +51078,7 @@ function writeStationHydration(options) {
50790
51078
  const toWrite = [];
50791
51079
  for (const skill of plan.winners) {
50792
51080
  for (const file2 of skill.files) {
50793
- const destination = join47(cacheRoot, skill.ident, file2.withinIdent);
51081
+ const destination = join48(cacheRoot, skill.ident, file2.withinIdent);
50794
51082
  const digest4 = sha256File(file2.winner.fullPath);
50795
51083
  let existingDigest = null;
50796
51084
  try {
@@ -50811,7 +51099,7 @@ function writeStationHydration(options) {
50811
51099
  }
50812
51100
  let written = 0;
50813
51101
  for (const entry of toWrite) {
50814
- mkdirSync19(dirname33(entry.destination), { recursive: true });
51102
+ mkdirSync19(dirname34(entry.destination), { recursive: true });
50815
51103
  copyFileSync2(entry.fullPath, entry.destination);
50816
51104
  written += 1;
50817
51105
  }
@@ -50832,8 +51120,8 @@ function writeStationHydration(options) {
50832
51120
  },
50833
51121
  skills: resultSkills
50834
51122
  };
50835
- const hydrationManifestPath = join47(dirname33(cacheRoot), `hydration-${options.stationId}.json`);
50836
- mkdirSync19(dirname33(hydrationManifestPath), { recursive: true });
51123
+ const hydrationManifestPath = join48(dirname34(cacheRoot), `hydration-${options.stationId}.json`);
51124
+ mkdirSync19(dirname34(hydrationManifestPath), { recursive: true });
50837
51125
  writeFileSync16(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
50838
51126
  `);
50839
51127
  return {
@@ -50844,23 +51132,23 @@ function writeStationHydration(options) {
50844
51132
  };
50845
51133
  }
50846
51134
  // src/lib/private-publication-recovery.ts
50847
- import { constants as constants21, closeSync as closeSync20, fsyncSync as fsyncSync8, fstatSync as fstatSync20, lstatSync as lstatSync33, mkdirSync as mkdirSync20, openSync as openSync20, readSync as readSync15, realpathSync as realpathSync16, renameSync as renameSync8, unlinkSync as unlinkSync6, writeFileSync as writeFileSync17 } from "fs";
50848
- import { dirname as dirname34, isAbsolute as isAbsolute32, join as join48, resolve as resolve31 } from "path";
51135
+ import { constants as constants21, closeSync as closeSync20, fsyncSync as fsyncSync8, fstatSync as fstatSync20, lstatSync as lstatSync34, mkdirSync as mkdirSync20, openSync as openSync20, readSync as readSync15, realpathSync as realpathSync16, renameSync as renameSync8, unlinkSync as unlinkSync6, writeFileSync as writeFileSync17 } from "fs";
51136
+ import { dirname as dirname35, isAbsolute as isAbsolute32, join as join49, resolve as resolve32 } from "path";
50849
51137
  import { randomUUID as randomUUID5 } from "crypto";
50850
51138
  var fail3 = () => {
50851
51139
  throw new PrivatePublicationError("PUBLICATION_RECOVERY_INVALID", "The recovery directory is invalid or changed. Preserve it and inspect the existing intent; do not start a replacement automatically.");
50852
51140
  };
50853
51141
  function safeDirectory(directory3) {
50854
- if (!isAbsolute32(directory3) || directory3 !== resolve31(directory3) || realpathSync16(directory3) !== directory3)
51142
+ if (!isAbsolute32(directory3) || directory3 !== resolve32(directory3) || realpathSync16(directory3) !== directory3)
50855
51143
  return fail3();
50856
- for (let path = directory3;; path = dirname34(path)) {
50857
- const stat2 = lstatSync33(path);
51144
+ for (let path = directory3;; path = dirname35(path)) {
51145
+ const stat2 = lstatSync34(path);
50858
51146
  if (!stat2.isDirectory() || stat2.isSymbolicLink())
50859
51147
  return fail3();
50860
- if (path === dirname34(path))
51148
+ if (path === dirname35(path))
50861
51149
  break;
50862
51150
  }
50863
- const own2 = lstatSync33(directory3);
51151
+ const own2 = lstatSync34(directory3);
50864
51152
  if ((own2.mode & 63) !== 0 || process.getuid && own2.uid !== process.getuid())
50865
51153
  return fail3();
50866
51154
  return { dev: own2.dev, ino: own2.ino };
@@ -50871,7 +51159,7 @@ function unchangedDirectory(directory3, identity5) {
50871
51159
  return fail3();
50872
51160
  }
50873
51161
  function readOwned(directory3, name, max) {
50874
- const identity5 = safeDirectory(directory3), file2 = join48(directory3, name);
51162
+ const identity5 = safeDirectory(directory3), file2 = join49(directory3, name);
50875
51163
  const fd = openSync20(file2, constants21.O_RDONLY | constants21.O_NOFOLLOW);
50876
51164
  try {
50877
51165
  const stat2 = fstatSync20(fd);
@@ -50895,7 +51183,7 @@ function readOwned(directory3, name, max) {
50895
51183
  }
50896
51184
  }
50897
51185
  function writeOwned(directory3, name, bytes) {
50898
- const fd = openSync20(join48(directory3, name), constants21.O_WRONLY | constants21.O_CREAT | constants21.O_EXCL | constants21.O_NOFOLLOW, 384);
51186
+ const fd = openSync20(join49(directory3, name), constants21.O_WRONLY | constants21.O_CREAT | constants21.O_EXCL | constants21.O_NOFOLLOW, 384);
50899
51187
  try {
50900
51188
  writeFileSync17(fd, bytes);
50901
51189
  fsyncSync8(fd);
@@ -50908,7 +51196,7 @@ function save(directory3, value) {
50908
51196
  writeOwned(directory3, temporary, JSON.stringify(value) + `
50909
51197
  `);
50910
51198
  unchangedDirectory(directory3, identity5);
50911
- renameSync8(join48(directory3, temporary), join48(directory3, "receipt.json"));
51199
+ renameSync8(join49(directory3, temporary), join49(directory3, "receipt.json"));
50912
51200
  const fd = openSync20(directory3, constants21.O_RDONLY | constants21.O_NOFOLLOW);
50913
51201
  try {
50914
51202
  fsyncSync8(fd);
@@ -50921,7 +51209,7 @@ function bind(client2, receipt) {
50921
51209
  throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
50922
51210
  }
50923
51211
  async function locked(directory3, action) {
50924
- const identity5 = safeDirectory(directory3), file2 = join48(directory3, "operation.lock");
51212
+ const identity5 = safeDirectory(directory3), file2 = join49(directory3, "operation.lock");
50925
51213
  let fd;
50926
51214
  try {
50927
51215
  fd = openSync20(file2, constants21.O_WRONLY | constants21.O_CREAT | constants21.O_EXCL | constants21.O_NOFOLLOW, 384);
@@ -50937,7 +51225,7 @@ async function locked(directory3, action) {
50937
51225
  } finally {
50938
51226
  closeSync20(fd);
50939
51227
  unchangedDirectory(directory3, identity5);
50940
- const now = lstatSync33(file2);
51228
+ const now = lstatSync34(file2);
50941
51229
  if (now.dev !== lock.dev || now.ino !== lock.ino || !now.isFile() || now.isSymbolicLink())
50942
51230
  fail3();
50943
51231
  unlinkSync6(file2);
@@ -50989,11 +51277,11 @@ async function preparePrivatePublication(client2, sourceDirectory, recoveryDirec
50989
51277
  phase: "prepared",
50990
51278
  intent: null
50991
51279
  };
50992
- if (!isAbsolute32(recoveryDirectory) || resolve31(recoveryDirectory) !== recoveryDirectory || realpathSync16(dirname34(recoveryDirectory)) !== dirname34(recoveryDirectory))
51280
+ if (!isAbsolute32(recoveryDirectory) || resolve32(recoveryDirectory) !== recoveryDirectory || realpathSync16(dirname35(recoveryDirectory)) !== dirname35(recoveryDirectory))
50993
51281
  return fail3();
50994
51282
  mkdirSync20(recoveryDirectory, { mode: 448 });
50995
51283
  safeDirectory(recoveryDirectory);
50996
- const parent = openSync20(dirname34(recoveryDirectory), constants21.O_RDONLY | constants21.O_NOFOLLOW);
51284
+ const parent = openSync20(dirname35(recoveryDirectory), constants21.O_RDONLY | constants21.O_NOFOLLOW);
50997
51285
  try {
50998
51286
  fsyncSync8(parent);
50999
51287
  } finally {
@@ -51081,7 +51369,7 @@ async function inspectLocked(client2, directory3, cancel) {
51081
51369
  }
51082
51370
  // src/lib/skill-context.ts
51083
51371
  import { createHash as createHash38 } from "crypto";
51084
- import { resolve as resolve32 } from "path";
51372
+ import { resolve as resolve33 } from "path";
51085
51373
  function words(value) {
51086
51374
  return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
51087
51375
  }
@@ -51120,7 +51408,7 @@ async function buildSkillContext(input2, options = {}) {
51120
51408
  const promptWords = words(prompt);
51121
51409
  const promptNames = new Set(prompt.toLowerCase().match(/[a-z0-9]+(?:-[a-z0-9]+)*/g) ?? []);
51122
51410
  const loaded = new Set(resolved.session?.loaded ?? []);
51123
- const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve32(input2.cwd)] : []];
51411
+ const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve33(input2.cwd)] : []];
51124
51412
  const candidates = profile.selections.map((selection) => {
51125
51413
  const key2 = selectionKey(selection);
51126
51414
  if (input2.restore && loaded.has(key2))
@@ -51315,8 +51603,8 @@ function applySessionReconciliation(snapshot2, target, planned, record12, verifi
51315
51603
  }
51316
51604
  // src/lib/claude-prospective-review.ts
51317
51605
  import { createHash as createHash40 } from "crypto";
51318
- import { lstatSync as lstatSync34, readFileSync as readFileSync33, readdirSync as readdirSync19, realpathSync as realpathSync17 } from "fs";
51319
- import { basename as basename18, dirname as dirname35, isAbsolute as isAbsolute33, join as join49, relative as relative16, resolve as resolve33, sep as sep21 } from "path";
51606
+ import { lstatSync as lstatSync35, readFileSync as readFileSync34, readdirSync as readdirSync19, realpathSync as realpathSync17 } from "fs";
51607
+ import { basename as basename18, dirname as dirname36, isAbsolute as isAbsolute33, join as join50, relative as relative16, resolve as resolve34, sep as sep22 } from "path";
51320
51608
  var MAX_CANDIDATE_FILES = 16384;
51321
51609
  var MAX_CANDIDATE_BYTES = 256 * 1024 * 1024;
51322
51610
  var sha7 = (data) => createHash40("sha256").update(data).digest("hex");
@@ -51327,32 +51615,32 @@ var GENERATED_MARKETPLACE_TIME = Object.freeze({ $generated: "claude-utc-timesta
51327
51615
  var GENERATED_FRESH_PLUGIN_INSTALLED_AT = Object.freeze({ $generated: "claude-utc-timestamp", field: "installed_plugins.fresh-row.installedAt" });
51328
51616
  var GENERATED_PLUGIN_UPDATE_TIME = Object.freeze({ $generated: "claude-utc-timestamp", field: "installed_plugins.selected-row.lastUpdated" });
51329
51617
  function abs(path, name) {
51330
- if (typeof path !== "string" || !path || path.length > 4096 || /[\x00-\x1f\x7f]/.test(path) || !isAbsolute33(path) || resolve33(path) !== path)
51618
+ if (typeof path !== "string" || !path || path.length > 4096 || /[\x00-\x1f\x7f]/.test(path) || !isAbsolute33(path) || resolve34(path) !== path)
51331
51619
  refuse6(`${name} must be a normalized absolute path`);
51332
51620
  }
51333
51621
  function safeFile(path, name) {
51334
51622
  abs(path, name);
51335
- for (let at = dirname35(path);; at = dirname35(at)) {
51336
- const stat3 = lstatSync34(at, { throwIfNoEntry: false });
51623
+ for (let at = dirname36(path);; at = dirname36(at)) {
51624
+ const stat3 = lstatSync35(at, { throwIfNoEntry: false });
51337
51625
  if (!stat3?.isDirectory() || stat3.isSymbolicLink())
51338
51626
  refuse6(`${name} traverses a symlink or non-directory`);
51339
- if (at === dirname35(at))
51627
+ if (at === dirname36(at))
51340
51628
  break;
51341
51629
  }
51342
- const stat2 = lstatSync34(path, { throwIfNoEntry: false });
51630
+ const stat2 = lstatSync35(path, { throwIfNoEntry: false });
51343
51631
  if (!stat2?.isFile() || stat2.isSymbolicLink() || stat2.size > MAX_CANDIDATE_BYTES)
51344
51632
  refuse6(`${name} is not a bounded regular file`);
51345
- return readFileSync33(path);
51633
+ return readFileSync34(path);
51346
51634
  }
51347
51635
  function canonicalDir(path, name) {
51348
51636
  abs(path, name);
51349
- if (realpathSync17(path) !== path || !lstatSync34(path).isDirectory())
51637
+ if (realpathSync17(path) !== path || !lstatSync35(path).isDirectory())
51350
51638
  refuse6(`${name} is not a canonical directory`);
51351
- for (let at = path;; at = dirname35(at)) {
51352
- const stat2 = lstatSync34(at);
51639
+ for (let at = path;; at = dirname36(at)) {
51640
+ const stat2 = lstatSync35(at);
51353
51641
  if (!stat2.isDirectory() || stat2.isSymbolicLink())
51354
51642
  refuse6(`${name} traverses a symlink`);
51355
- if (at === dirname35(at))
51643
+ if (at === dirname36(at))
51356
51644
  break;
51357
51645
  }
51358
51646
  }
@@ -51361,17 +51649,17 @@ function captureClaudeProspectiveCandidateClosure(root2) {
51361
51649
  const entries = [];
51362
51650
  let bytes = 0;
51363
51651
  const walk2 = (directory3) => {
51364
- const beforeDirectory = lstatSync34(directory3);
51652
+ const beforeDirectory = lstatSync35(directory3);
51365
51653
  const names = readdirSync19(directory3).sort();
51366
51654
  if (new Set(names).size !== names.length)
51367
51655
  refuse6("candidate contains ambiguous directory entries");
51368
51656
  for (const name of names) {
51369
51657
  if (!name || name === "." || name === ".." || name.includes("/") || name.includes("\\") || /[\x00-\x1f\x7f]/.test(name))
51370
51658
  refuse6("candidate contains an unsupported path member");
51371
- const path = join49(directory3, name), rel = relative16(root2, path).split(sep21).join("/");
51659
+ const path = join50(directory3, name), rel = relative16(root2, path).split(sep22).join("/");
51372
51660
  if (entries.length >= MAX_CANDIDATE_FILES)
51373
51661
  refuse6("candidate closure exceeds its member limit");
51374
- const stat2 = lstatSync34(path);
51662
+ const stat2 = lstatSync35(path);
51375
51663
  if (stat2.isSymbolicLink())
51376
51664
  refuse6("candidate closure contains a symlink");
51377
51665
  if (stat2.isDirectory()) {
@@ -51381,14 +51669,14 @@ function captureClaudeProspectiveCandidateClosure(root2) {
51381
51669
  bytes += stat2.size;
51382
51670
  if (bytes > MAX_CANDIDATE_BYTES)
51383
51671
  refuse6("candidate closure exceeds its byte limit");
51384
- const content = safeFile(path, "candidate member"), after = lstatSync34(path);
51672
+ const content = safeFile(path, "candidate member"), after = lstatSync35(path);
51385
51673
  if (!after.isFile() || after.dev !== stat2.dev || after.ino !== stat2.ino || after.size !== stat2.size || after.mtimeMs !== stat2.mtimeMs || after.ctimeMs !== stat2.ctimeMs)
51386
51674
  refuse6("candidate member changed while being read");
51387
51675
  entries.push([rel, "file", sha7(content)]);
51388
51676
  } else
51389
51677
  refuse6("candidate closure contains a special file");
51390
51678
  }
51391
- const afterDirectory = lstatSync34(directory3);
51679
+ const afterDirectory = lstatSync35(directory3);
51392
51680
  if (!afterDirectory.isDirectory() || afterDirectory.dev !== beforeDirectory.dev || afterDirectory.ino !== beforeDirectory.ino || afterDirectory.mtimeMs !== beforeDirectory.mtimeMs || afterDirectory.ctimeMs !== beforeDirectory.ctimeMs)
51393
51681
  refuse6("candidate directory changed while being read");
51394
51682
  };
@@ -51396,6 +51684,21 @@ function captureClaudeProspectiveCandidateClosure(root2) {
51396
51684
  entries.sort((a, b) => a[0].localeCompare(b[0]));
51397
51685
  return sha7(JSON.stringify(entries));
51398
51686
  }
51687
+ var EXEMPT_PROJECT_SETTINGS = new Map([
51688
+ ["permissions", "object"],
51689
+ ["outputStyle", "string"],
51690
+ ["model", "string"],
51691
+ ["theme", "string"],
51692
+ ["effortLevel", "string"],
51693
+ ["verbose", "boolean"],
51694
+ ["spinnerTipsEnabled", "boolean"]
51695
+ ]);
51696
+ var EXEMPT_PROJECT_PERMISSIONS = new Map([
51697
+ ["allow", "strings"],
51698
+ ["deny", "strings"],
51699
+ ["ask", "strings"],
51700
+ ["defaultMode", "string"]
51701
+ ]);
51399
51702
  export {
51400
51703
  writeStationSnapshot,
51401
51704
  writeStationHydration,