@hasna/skills 0.10.46 → 0.10.48

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
@@ -3724,10 +3724,10 @@ var require_resolve_block_map = __commonJS((exports) => {
3724
3724
  let offset = bm.offset;
3725
3725
  let commentEnd = null;
3726
3726
  for (const collItem of bm.items) {
3727
- const { start, key, sep: sep2, value } = collItem;
3727
+ const { start, key, sep: sep3, value } = collItem;
3728
3728
  const keyProps = resolveProps.resolveProps(start, {
3729
3729
  indicator: "explicit-key-ind",
3730
- next: key ?? sep2?.[0],
3730
+ next: key ?? sep3?.[0],
3731
3731
  offset,
3732
3732
  onError,
3733
3733
  parentIndent: bm.indent,
@@ -3741,7 +3741,7 @@ var require_resolve_block_map = __commonJS((exports) => {
3741
3741
  else if ("indent" in key && key.indent !== bm.indent)
3742
3742
  onError(offset, "BAD_INDENT", startColMsg);
3743
3743
  }
3744
- if (!keyProps.anchor && !keyProps.tag && !sep2) {
3744
+ if (!keyProps.anchor && !keyProps.tag && !sep3) {
3745
3745
  commentEnd = keyProps.end;
3746
3746
  if (keyProps.comment) {
3747
3747
  if (map.comment)
@@ -3766,7 +3766,7 @@ var require_resolve_block_map = __commonJS((exports) => {
3766
3766
  ctx.atKey = false;
3767
3767
  if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
3768
3768
  onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
3769
- const valueProps = resolveProps.resolveProps(sep2 ?? [], {
3769
+ const valueProps = resolveProps.resolveProps(sep3 ?? [], {
3770
3770
  indicator: "map-value-ind",
3771
3771
  next: value,
3772
3772
  offset: keyNode.range[2],
@@ -3782,7 +3782,7 @@ var require_resolve_block_map = __commonJS((exports) => {
3782
3782
  if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
3783
3783
  onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
3784
3784
  }
3785
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep2, null, valueProps, onError);
3785
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep3, null, valueProps, onError);
3786
3786
  if (ctx.schema.compat)
3787
3787
  utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
3788
3788
  offset = valueNode.range[2];
@@ -3868,7 +3868,7 @@ var require_resolve_end = __commonJS((exports) => {
3868
3868
  let comment = "";
3869
3869
  if (end) {
3870
3870
  let hasSpace = false;
3871
- let sep2 = "";
3871
+ let sep3 = "";
3872
3872
  for (const token of end) {
3873
3873
  const { source, type } = token;
3874
3874
  switch (type) {
@@ -3882,13 +3882,13 @@ var require_resolve_end = __commonJS((exports) => {
3882
3882
  if (!comment)
3883
3883
  comment = cb;
3884
3884
  else
3885
- comment += sep2 + cb;
3886
- sep2 = "";
3885
+ comment += sep3 + cb;
3886
+ sep3 = "";
3887
3887
  break;
3888
3888
  }
3889
3889
  case "newline":
3890
3890
  if (comment)
3891
- sep2 += source;
3891
+ sep3 += source;
3892
3892
  hasSpace = true;
3893
3893
  break;
3894
3894
  default:
@@ -3928,18 +3928,18 @@ var require_resolve_flow_collection = __commonJS((exports) => {
3928
3928
  let offset = fc.offset + fc.start.source.length;
3929
3929
  for (let i = 0;i < fc.items.length; ++i) {
3930
3930
  const collItem = fc.items[i];
3931
- const { start, key, sep: sep2, value } = collItem;
3931
+ const { start, key, sep: sep3, value } = collItem;
3932
3932
  const props = resolveProps.resolveProps(start, {
3933
3933
  flow: fcName,
3934
3934
  indicator: "explicit-key-ind",
3935
- next: key ?? sep2?.[0],
3935
+ next: key ?? sep3?.[0],
3936
3936
  offset,
3937
3937
  onError,
3938
3938
  parentIndent: fc.indent,
3939
3939
  startOnNewline: false
3940
3940
  });
3941
3941
  if (!props.found) {
3942
- if (!props.anchor && !props.tag && !sep2 && !value) {
3942
+ if (!props.anchor && !props.tag && !sep3 && !value) {
3943
3943
  if (i === 0 && props.comma)
3944
3944
  onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
3945
3945
  else if (i < fc.items.length - 1)
@@ -3991,8 +3991,8 @@ var require_resolve_flow_collection = __commonJS((exports) => {
3991
3991
  }
3992
3992
  }
3993
3993
  }
3994
- if (!isMap && !sep2 && !props.found) {
3995
- const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep2, null, props, onError);
3994
+ if (!isMap && !sep3 && !props.found) {
3995
+ const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep3, null, props, onError);
3996
3996
  coll.items.push(valueNode);
3997
3997
  offset = valueNode.range[2];
3998
3998
  if (isBlock(value))
@@ -4004,7 +4004,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
4004
4004
  if (isBlock(key))
4005
4005
  onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
4006
4006
  ctx.atKey = false;
4007
- const valueProps = resolveProps.resolveProps(sep2 ?? [], {
4007
+ const valueProps = resolveProps.resolveProps(sep3 ?? [], {
4008
4008
  flow: fcName,
4009
4009
  indicator: "map-value-ind",
4010
4010
  next: value,
@@ -4015,8 +4015,8 @@ var require_resolve_flow_collection = __commonJS((exports) => {
4015
4015
  });
4016
4016
  if (valueProps.found) {
4017
4017
  if (!isMap && !props.found && ctx.options.strict) {
4018
- if (sep2)
4019
- for (const st of sep2) {
4018
+ if (sep3)
4019
+ for (const st of sep3) {
4020
4020
  if (st === valueProps.found)
4021
4021
  break;
4022
4022
  if (st.type === "newline") {
@@ -4033,7 +4033,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
4033
4033
  else
4034
4034
  onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
4035
4035
  }
4036
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep2, null, valueProps, onError) : null;
4036
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep3, null, valueProps, onError) : null;
4037
4037
  if (valueNode) {
4038
4038
  if (isBlock(value))
4039
4039
  onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
@@ -4210,7 +4210,7 @@ var require_resolve_block_scalar = __commonJS((exports) => {
4210
4210
  chompStart = i + 1;
4211
4211
  }
4212
4212
  let value = "";
4213
- let sep2 = "";
4213
+ let sep3 = "";
4214
4214
  let prevMoreIndented = false;
4215
4215
  for (let i = 0;i < contentStart; ++i)
4216
4216
  value += lines[i][0].slice(trimIndent) + `
@@ -4228,33 +4228,33 @@ var require_resolve_block_scalar = __commonJS((exports) => {
4228
4228
  indent = "";
4229
4229
  }
4230
4230
  if (type === Scalar.Scalar.BLOCK_LITERAL) {
4231
- value += sep2 + indent.slice(trimIndent) + content;
4232
- sep2 = `
4231
+ value += sep3 + indent.slice(trimIndent) + content;
4232
+ sep3 = `
4233
4233
  `;
4234
4234
  } else if (indent.length > trimIndent || content[0] === "\t") {
4235
- if (sep2 === " ")
4236
- sep2 = `
4235
+ if (sep3 === " ")
4236
+ sep3 = `
4237
4237
  `;
4238
- else if (!prevMoreIndented && sep2 === `
4238
+ else if (!prevMoreIndented && sep3 === `
4239
4239
  `)
4240
- sep2 = `
4240
+ sep3 = `
4241
4241
 
4242
4242
  `;
4243
- value += sep2 + indent.slice(trimIndent) + content;
4244
- sep2 = `
4243
+ value += sep3 + indent.slice(trimIndent) + content;
4244
+ sep3 = `
4245
4245
  `;
4246
4246
  prevMoreIndented = true;
4247
4247
  } else if (content === "") {
4248
- if (sep2 === `
4248
+ if (sep3 === `
4249
4249
  `)
4250
4250
  value += `
4251
4251
  `;
4252
4252
  else
4253
- sep2 = `
4253
+ sep3 = `
4254
4254
  `;
4255
4255
  } else {
4256
- value += sep2 + content;
4257
- sep2 = " ";
4256
+ value += sep3 + content;
4257
+ sep3 = " ";
4258
4258
  prevMoreIndented = false;
4259
4259
  }
4260
4260
  }
@@ -4435,27 +4435,27 @@ var require_resolve_flow_scalar = __commonJS((exports) => {
4435
4435
  if (!match)
4436
4436
  return source;
4437
4437
  let res = match[1];
4438
- let sep2 = " ";
4438
+ let sep3 = " ";
4439
4439
  let pos = first.lastIndex;
4440
4440
  line.lastIndex = pos;
4441
4441
  while (match = line.exec(source)) {
4442
4442
  if (match[1] === "") {
4443
- if (sep2 === `
4443
+ if (sep3 === `
4444
4444
  `)
4445
- res += sep2;
4445
+ res += sep3;
4446
4446
  else
4447
- sep2 = `
4447
+ sep3 = `
4448
4448
  `;
4449
4449
  } else {
4450
- res += sep2 + match[1];
4451
- sep2 = " ";
4450
+ res += sep3 + match[1];
4451
+ sep3 = " ";
4452
4452
  }
4453
4453
  pos = line.lastIndex;
4454
4454
  }
4455
4455
  const last = /[ \t]*(.*)/sy;
4456
4456
  last.lastIndex = pos;
4457
4457
  match = last.exec(source);
4458
- return res + sep2 + (match?.[1] ?? "");
4458
+ return res + sep3 + (match?.[1] ?? "");
4459
4459
  }
4460
4460
  function doubleQuotedValue(source, onError) {
4461
4461
  let res = "";
@@ -5236,14 +5236,14 @@ var require_cst_stringify = __commonJS((exports) => {
5236
5236
  }
5237
5237
  }
5238
5238
  }
5239
- function stringifyItem({ start, key, sep: sep2, value }) {
5239
+ function stringifyItem({ start, key, sep: sep3, value }) {
5240
5240
  let res = "";
5241
5241
  for (const st of start)
5242
5242
  res += st.source;
5243
5243
  if (key)
5244
5244
  res += stringifyToken(key);
5245
- if (sep2)
5246
- for (const st of sep2)
5245
+ if (sep3)
5246
+ for (const st of sep3)
5247
5247
  res += st.source;
5248
5248
  if (value)
5249
5249
  res += stringifyToken(value);
@@ -6391,18 +6391,18 @@ var require_parser = __commonJS((exports) => {
6391
6391
  if (this.type === "map-value-ind") {
6392
6392
  const prev = getPrevProps(this.peek(2));
6393
6393
  const start = getFirstKeyStartProps(prev);
6394
- let sep2;
6394
+ let sep3;
6395
6395
  if (scalar.end) {
6396
- sep2 = scalar.end;
6397
- sep2.push(this.sourceToken);
6396
+ sep3 = scalar.end;
6397
+ sep3.push(this.sourceToken);
6398
6398
  delete scalar.end;
6399
6399
  } else
6400
- sep2 = [this.sourceToken];
6400
+ sep3 = [this.sourceToken];
6401
6401
  const map = {
6402
6402
  type: "block-map",
6403
6403
  offset: scalar.offset,
6404
6404
  indent: scalar.indent,
6405
- items: [{ start, key: scalar, sep: sep2 }]
6405
+ items: [{ start, key: scalar, sep: sep3 }]
6406
6406
  };
6407
6407
  this.onKeyLine = true;
6408
6408
  this.stack[this.stack.length - 1] = map;
@@ -6556,15 +6556,15 @@ var require_parser = __commonJS((exports) => {
6556
6556
  } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
6557
6557
  const start2 = getFirstKeyStartProps(it.start);
6558
6558
  const key = it.key;
6559
- const sep2 = it.sep;
6560
- sep2.push(this.sourceToken);
6559
+ const sep3 = it.sep;
6560
+ sep3.push(this.sourceToken);
6561
6561
  delete it.key;
6562
6562
  delete it.sep;
6563
6563
  this.stack.push({
6564
6564
  type: "block-map",
6565
6565
  offset: this.offset,
6566
6566
  indent: this.indent,
6567
- items: [{ start: start2, key, sep: sep2 }]
6567
+ items: [{ start: start2, key, sep: sep3 }]
6568
6568
  });
6569
6569
  } else if (start.length > 0) {
6570
6570
  it.sep = it.sep.concat(start, this.sourceToken);
@@ -6758,13 +6758,13 @@ var require_parser = __commonJS((exports) => {
6758
6758
  const prev = getPrevProps(parent);
6759
6759
  const start = getFirstKeyStartProps(prev);
6760
6760
  fixFlowSeqItems(fc);
6761
- const sep2 = fc.end.splice(1, fc.end.length);
6762
- sep2.push(this.sourceToken);
6761
+ const sep3 = fc.end.splice(1, fc.end.length);
6762
+ sep3.push(this.sourceToken);
6763
6763
  const map = {
6764
6764
  type: "block-map",
6765
6765
  offset: fc.offset,
6766
6766
  indent: fc.indent,
6767
- items: [{ start, key: fc, sep: sep2 }]
6767
+ items: [{ start, key: fc, sep: sep3 }]
6768
6768
  };
6769
6769
  this.onKeyLine = true;
6770
6770
  this.stack[this.stack.length - 1] = map;
@@ -6989,7 +6989,7 @@ var require_public_api = __commonJS((exports) => {
6989
6989
 
6990
6990
  // src/lib/registry.ts
6991
6991
  import { existsSync as existsSync15, readFileSync as readFileSync20, readdirSync as readdirSync12 } from "fs";
6992
- import { join as join34 } from "path";
6992
+ import { join as join35 } from "path";
6993
6993
 
6994
6994
  // src/lib/config.ts
6995
6995
  import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "fs";
@@ -7262,7 +7262,7 @@ import {
7262
7262
  statSync as statSync11,
7263
7263
  writeFileSync as writeFileSync9
7264
7264
  } from "fs";
7265
- import { basename as basename12, dirname as dirname21, isAbsolute as isAbsolute19, join as join33, normalize as normalize4, relative as relative7, resolve as resolve20, sep as sep9 } from "path";
7265
+ import { basename as basename12, dirname as dirname21, isAbsolute as isAbsolute19, join as join34, normalize as normalize4, relative as relative7, resolve as resolve20, sep as sep10 } from "path";
7266
7266
 
7267
7267
  // src/lib/registry-data/index.ts
7268
7268
  var SKILLS = [];
@@ -8922,7 +8922,7 @@ var NATIVE_SKILL_ROOTS = [
8922
8922
  // src/lib/authoring-path.ts
8923
8923
  import { lstatSync as lstatSync25, realpathSync as realpathSync11 } from "fs";
8924
8924
  import { homedir as homedir6 } from "os";
8925
- import { basename as basename11, dirname as dirname20, join as join32, resolve as resolve19, sep as sep8 } from "path";
8925
+ import { basename as basename11, dirname as dirname20, join as join33, resolve as resolve19, sep as sep9 } from "path";
8926
8926
 
8927
8927
  // src/lib/agent-adapters.ts
8928
8928
  var INTEGRATION_AGENTS = ["claude", "codex", "gemini", "opencode", "cursor", "hermes", "sumi"];
@@ -9355,7 +9355,7 @@ function upgradeSumiSettingsWitness(previous, reviewedSettingsPath) {
9355
9355
  }
9356
9356
 
9357
9357
  // src/lib/agent-policy-limits.ts
9358
- import { basename as basename3, isAbsolute as isAbsolute3, resolve as resolve3 } from "path";
9358
+ import { basename as basename3, isAbsolute as isAbsolute3, join as join7, resolve as resolve3, sep as sep2 } from "path";
9359
9359
  var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryPathLinks: 40, discoveryPathSteps: 256, discoveryPathSourceMetadataBytes: 64 * 1024, discoveryPathTotalMetadataBytes: 8 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
9360
9360
  function object(value) {
9361
9361
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -9497,7 +9497,9 @@ function assertAgentPolicyCollections(policy) {
9497
9497
  text(source.path);
9498
9498
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
9499
9499
  if (source.hashMode !== undefined)
9500
- requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
9500
+ requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "sumi-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
9501
+ if (source.hashMode === "claude-plugin-manifest-v1")
9502
+ requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && source.path.endsWith(join7(sep2, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
9501
9503
  if (source.hashMode === "sumi-settings-v1")
9502
9504
  requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve3(source.path) === source.path && basename3(source.path) === "sumi.json");
9503
9505
  if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4"))
@@ -9536,9 +9538,9 @@ function assertAgentPolicyCollections(policy) {
9536
9538
  }
9537
9539
 
9538
9540
  // src/lib/agent-discovery.ts
9539
- import { createHash as createHash22 } from "crypto";
9541
+ import { createHash as createHash23 } from "crypto";
9540
9542
  import { lstatSync as lstatSync24, readFileSync as readFileSync19, readdirSync as readdirSync10, realpathSync as realpathSync10 } from "fs";
9541
- import { basename as basename10, dirname as dirname19, isAbsolute as isAbsolute18, join as join31, resolve as resolve18, sep as sep7 } from "path";
9543
+ import { basename as basename10, dirname as dirname19, isAbsolute as isAbsolute18, join as join32, resolve as resolve18, sep as sep8 } from "path";
9542
9544
  import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
9543
9545
 
9544
9546
  // node_modules/yaml/dist/index.js
@@ -9588,7 +9590,7 @@ var $visit = visit.visit;
9588
9590
  var $visitAsync = visit.visitAsync;
9589
9591
 
9590
9592
  // src/lib/agent-hermes.ts
9591
- import { dirname as dirname4, isAbsolute as isAbsolute4, join as join7, resolve as resolve4 } from "path";
9593
+ import { dirname as dirname4, isAbsolute as isAbsolute4, join as join8, resolve as resolve4 } from "path";
9592
9594
  import { lstatSync as lstatSync4, readFileSync as readFileSync6, opendirSync } from "fs";
9593
9595
  var HERMES_OPT_OUT = ".no-bundled-skills";
9594
9596
  var EVENTS = ["pre_llm_call", "pre_tool_call"];
@@ -9662,7 +9664,7 @@ function assertHermesEnvironment(home) {
9662
9664
  throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
9663
9665
  }
9664
9666
  const selected = process.env.HERMES_HOME?.trim();
9665
- if (selected && (!isAbsolute4(selected) || resolve4(selected) !== join7(resolve4(home), ".hermes")))
9667
+ if (selected && (!isAbsolute4(selected) || resolve4(selected) !== join8(resolve4(home), ".hermes")))
9666
9668
  throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
9667
9669
  if (process.env.HERMES_ENABLE_PROJECT_PLUGINS?.trim() && !["0", "false", "no", "off"].includes(process.env.HERMES_ENABLE_PROJECT_PLUGINS.trim().toLowerCase()))
9668
9670
  throw new Error("NATIVE_SKILL_DRIFT: Hermes project plugins require a dedicated discovery review");
@@ -9683,11 +9685,11 @@ function read(path) {
9683
9685
  }
9684
9686
  function assertHermesProtection(home, config, command, profile, supervisor) {
9685
9687
  assertHermesEnvironment(home);
9686
- if (read(join7(home, ".hermes", HERMES_OPT_OUT)) === null)
9688
+ if (read(join8(home, ".hermes", HERMES_OPT_OUT)) === null)
9687
9689
  throw new Error("NATIVE_SKILL_DRIFT: Hermes bundled skill reseeding is not disabled; run skills hook install");
9688
9690
  if (!supervisor || !isAbsolute4(supervisor.path) || supervisor.runtime !== process.execPath || read(supervisor.path) !== renderHermesSupervisor(command, profile))
9689
9691
  throw new Error("NATIVE_SKILL_DRIFT: Hermes owned supervisor changed; run skills hook install");
9690
- const raw = read(join7(home, ".hermes/shell-hooks-allowlist.json"));
9692
+ const raw = read(join8(home, ".hermes/shell-hooks-allowlist.json"));
9691
9693
  let trust;
9692
9694
  try {
9693
9695
  trust = raw === null ? undefined : JSON.parse(raw);
@@ -9730,15 +9732,15 @@ function assertNoHermesLegacyShadow(root) {
9730
9732
  throw new Error("NATIVE_SKILL_DRIFT: unsupported Hermes legacy discovery input");
9731
9733
  return;
9732
9734
  }
9733
- if (lstatSync4(join7(path, "skills-cli.md"), { throwIfNoEntry: false }))
9735
+ if (lstatSync4(join8(path, "skills-cli.md"), { throwIfNoEntry: false }))
9734
9736
  throw new Error("NATIVE_SKILL_DRIFT: legacy Hermes skills-cli.md can shadow the owned bridge; preserve and retire it before continuing");
9735
- const hasDescriptor = Boolean(lstatSync4(join7(path, "SKILL.md"), { throwIfNoEntry: false }));
9737
+ const hasDescriptor = Boolean(lstatSync4(join8(path, "SKILL.md"), { throwIfNoEntry: false }));
9736
9738
  const directory = opendirSync(path);
9737
9739
  try {
9738
9740
  for (let child = directory.readSync();child; child = directory.readSync()) {
9739
9741
  if (hasDescriptor && ["references", "templates", "assets", "scripts"].includes(child.name))
9740
9742
  continue;
9741
- scan(join7(path, child.name), depth + 1);
9743
+ scan(join8(path, child.name), depth + 1);
9742
9744
  }
9743
9745
  } finally {
9744
9746
  directory.closeSync();
@@ -9797,7 +9799,7 @@ if (failed) process.exitCode = 2;
9797
9799
  // src/lib/agent-discovery-directories.ts
9798
9800
  import { createHash as createHash4 } from "crypto";
9799
9801
  import { lstatSync as lstatSync5, opendirSync as opendirSync2 } from "fs";
9800
- import { dirname as dirname5, isAbsolute as isAbsolute5, join as join8, resolve as resolve5 } from "path";
9802
+ import { dirname as dirname5, isAbsolute as isAbsolute5, join as join9, resolve as resolve5 } from "path";
9801
9803
  function need2(value, message) {
9802
9804
  if (!value)
9803
9805
  throw new Error(message);
@@ -9835,7 +9837,7 @@ function capture(path, budget2, excludedRoots, omittedRoots = [], rows) {
9835
9837
  }
9836
9838
  need2(new Set(names).size === names.length, "Ambiguous native discovery directory names");
9837
9839
  for (const name of names.sort()) {
9838
- const child = join8(current, name), childRelative = relative2 ? `${relative2}/${name}` : name;
9840
+ const child = join9(current, name), childRelative = relative2 ? `${relative2}/${name}` : name;
9839
9841
  if (omittedRoots.includes(child))
9840
9842
  continue;
9841
9843
  need2(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
@@ -9883,7 +9885,7 @@ function verifyDiscoveryDirectoryProjection(directories, omittedRoots) {
9883
9885
  need2(Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && value[0] && !isAbsolute5(value[0]) && !value[0].split("/").some((part) => !part || part === "." || part === "..") && ["file", "directory"].includes(value[1]) && row === JSON.stringify(value) + `
9884
9886
  `, "Invalid reviewed directory member");
9885
9887
  hash.update(row);
9886
- const path = join8(directory.path, value[0]);
9888
+ const path = join9(directory.path, value[0]);
9887
9889
  if (!omittedRoots.some((root) => path === root || path.startsWith(root + "/")))
9888
9890
  kept.push(row);
9889
9891
  }
@@ -9981,7 +9983,7 @@ function hashRawDiscoveryFile(path, budget2, changes) {
9981
9983
  // src/lib/agent-discovery-path-bytes.ts
9982
9984
  import { createHash as createHash6 } from "crypto";
9983
9985
  import { lstatSync as lstatSync7, readlinkSync } from "fs";
9984
- import { dirname as dirname7, isAbsolute as isAbsolute7, join as join9, parse as parse2, resolve as resolve7, sep as sep2 } from "path";
9986
+ import { dirname as dirname7, isAbsolute as isAbsolute7, join as join10, parse as parse2, resolve as resolve7, sep as sep3 } from "path";
9985
9987
  function identity2(stat, mutable = false) {
9986
9988
  return {
9987
9989
  dev: String(stat.dev),
@@ -9999,7 +10001,7 @@ function canonical2(path) {
9999
10001
  function trace(path) {
10000
10002
  canonical2(path);
10001
10003
  const rows = [], paths = [];
10002
- let metadataBytes = 0, steps = 0, links = 0, cursor = parse2(path).root, pending = path.slice(cursor.length).split(sep2);
10004
+ let metadataBytes = 0, steps = 0, links = 0, cursor = parse2(path).root, pending = path.slice(cursor.length).split(sep3);
10003
10005
  function record2(path2, row) {
10004
10006
  const value = { path: path2, ...row };
10005
10007
  metadataBytes += Buffer.byteLength(JSON.stringify(value));
@@ -10025,7 +10027,7 @@ function trace(path) {
10025
10027
  cursor = dirname7(cursor);
10026
10028
  continue;
10027
10029
  }
10028
- const candidate = join9(cursor, component);
10030
+ const candidate = join10(cursor, component);
10029
10031
  if (candidate.length > AGENT_POLICY_LIMITS.pathCharacters)
10030
10032
  throw new Error("Discovery resolved path length limit exceeded");
10031
10033
  const stat = lstatSync7(candidate, { bigint: true, throwIfNoEntry: false });
@@ -10047,7 +10049,7 @@ function trace(path) {
10047
10049
  root();
10048
10050
  }
10049
10051
  const remainder = isAbsolute7(target) ? target.slice(parse2(target).root.length) : target;
10050
- pending = [...remainder.split(sep2), ...pending];
10052
+ pending = [...remainder.split(sep3), ...pending];
10051
10053
  if (pending.length + steps > AGENT_POLICY_LIMITS.discoveryPathSteps)
10052
10054
  throw new Error("Discovery path step limit exceeded");
10053
10055
  continue;
@@ -10071,7 +10073,7 @@ function hashDiscoveryPathFile(path, budget2, changes) {
10071
10073
  let createsMissingPath = false;
10072
10074
  if (before.missing !== null && changes) {
10073
10075
  for (const target of changes.keys()) {
10074
- if (target === before.missing || target.startsWith(before.missing + sep2)) {
10076
+ if (target === before.missing || target.startsWith(before.missing + sep3)) {
10075
10077
  createsMissingPath = true;
10076
10078
  break;
10077
10079
  }
@@ -10092,26 +10094,161 @@ function hashDiscoveryPathFile(path, budget2, changes) {
10092
10094
  return createHash6("sha256").update(JSON.stringify({ version: 1, path, rows: before.rows, content })).digest("hex");
10093
10095
  }
10094
10096
 
10097
+ // src/lib/claude-plugin-manifest-witness.ts
10098
+ import { createHash as createHash7 } from "crypto";
10099
+ var CLAUDE_PLUGIN_MANIFEST_LIMITS = Object.freeze({ bytes: 1024 * 1024, depth: 32, nodes: 65536, stringCharacters: 16384 });
10100
+ function requireValue(value, reason) {
10101
+ if (!value)
10102
+ throw new Error("Claude plugin manifest " + reason);
10103
+ }
10104
+ function parse3(text2) {
10105
+ requireValue(Buffer.byteLength(text2, "utf8") <= CLAUDE_PLUGIN_MANIFEST_LIMITS.bytes, "exceeds its byte limit");
10106
+ let at = 0, nodes = 0;
10107
+ const numberPattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
10108
+ const space = () => {
10109
+ while (at < text2.length && /[\x20\t\r\n]/.test(text2[at]))
10110
+ at++;
10111
+ };
10112
+ function string() {
10113
+ requireValue(text2[at] === '"', "contains invalid JSON");
10114
+ const start = at++;
10115
+ while (at < text2.length) {
10116
+ const character = text2[at++];
10117
+ if (character === '"') {
10118
+ let value2;
10119
+ try {
10120
+ value2 = JSON.parse(text2.slice(start, at));
10121
+ } catch {
10122
+ throw new Error("Claude plugin manifest contains invalid JSON string");
10123
+ }
10124
+ requireValue(value2.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters, "exceeds its string limit");
10125
+ return value2;
10126
+ }
10127
+ if (character === "\\")
10128
+ at++;
10129
+ }
10130
+ throw new Error("Claude plugin manifest contains an unterminated JSON string");
10131
+ }
10132
+ function value(depth) {
10133
+ requireValue(depth <= CLAUDE_PLUGIN_MANIFEST_LIMITS.depth && ++nodes <= CLAUDE_PLUGIN_MANIFEST_LIMITS.nodes, "exceeds its nesting or node limit");
10134
+ space();
10135
+ const character = text2[at];
10136
+ if (character === '"')
10137
+ return { kind: "string", value: string() };
10138
+ if (character === "{" || character === "[") {
10139
+ at++;
10140
+ space();
10141
+ const object3 = character === "{", end = object3 ? "}" : "]", entries = [], items = [], keys = new Set;
10142
+ if (text2[at] !== end)
10143
+ while (true) {
10144
+ if (object3) {
10145
+ space();
10146
+ const key = string();
10147
+ requireValue(!keys.has(key), "contains a duplicate JSON key");
10148
+ keys.add(key);
10149
+ space();
10150
+ requireValue(text2[at++] === ":", "contains invalid JSON");
10151
+ entries.push([key, value(depth + 1)]);
10152
+ } else
10153
+ items.push(value(depth + 1));
10154
+ space();
10155
+ if (text2[at] === end)
10156
+ break;
10157
+ requireValue(text2[at++] === ",", "contains invalid JSON");
10158
+ }
10159
+ at++;
10160
+ return object3 ? { kind: "object", entries } : { kind: "array", items };
10161
+ }
10162
+ for (const literal of ["true", "false", "null"])
10163
+ if (text2.startsWith(literal, at)) {
10164
+ at += literal.length;
10165
+ return { kind: "literal", value: literal };
10166
+ }
10167
+ numberPattern.lastIndex = at;
10168
+ const number = numberPattern.exec(text2);
10169
+ requireValue(number, "contains invalid JSON");
10170
+ at += number[0].length;
10171
+ return { kind: "number", value: number[0] };
10172
+ }
10173
+ const root = value(0);
10174
+ space();
10175
+ requireValue(at === text2.length && root.kind === "object", "requires a JSON object without trailing content");
10176
+ return root;
10177
+ }
10178
+ function canonical3(value) {
10179
+ if (value.kind === "string")
10180
+ return JSON.stringify(value.value);
10181
+ if (value.kind === "number")
10182
+ return value.value;
10183
+ if (value.kind === "literal")
10184
+ return value.value;
10185
+ if (value.kind === "array")
10186
+ return "[" + value.items.map(canonical3).join(",") + "]";
10187
+ return "{" + value.entries.map(([key, item]) => JSON.stringify(key) + ":" + canonical3(item)).join(",") + "}";
10188
+ }
10189
+ var DESCRIPTIVE_FIELDS = new Set(["description", "version", "author", "homepage", "repository", "license", "keywords"]);
10190
+ function validateText(value, field) {
10191
+ requireValue(value?.kind === "string" && value.value.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters, "has an invalid " + field + " field");
10192
+ }
10193
+ function validateDescriptiveFields(manifest) {
10194
+ const values = new Map(manifest.entries);
10195
+ for (const field of ["description", "version", "homepage", "license"]) {
10196
+ const value = values.get(field);
10197
+ if (value !== undefined)
10198
+ validateText(value, field);
10199
+ }
10200
+ const author = values.get("author");
10201
+ if (author !== undefined) {
10202
+ if (author.kind === "string")
10203
+ validateText(author, "author");
10204
+ else {
10205
+ requireValue(author.kind === "object" && author.entries.every(([key, value]) => ["name", "email", "url"].includes(key) && value.kind === "string" && value.value.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters), "has an invalid author field");
10206
+ }
10207
+ }
10208
+ const repository = values.get("repository");
10209
+ if (repository !== undefined) {
10210
+ if (repository.kind === "string")
10211
+ validateText(repository, "repository");
10212
+ else
10213
+ requireValue(repository.kind === "object" && repository.entries.every(([key, value]) => ["type", "url", "directory"].includes(key) && value.kind === "string" && value.value.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters), "has an invalid repository field");
10214
+ }
10215
+ const keywords = values.get("keywords");
10216
+ if (keywords !== undefined)
10217
+ requireValue(keywords.kind === "array" && keywords.items.every((value) => value.kind === "string" && value.value.length <= CLAUDE_PLUGIN_MANIFEST_LIMITS.stringCharacters), "has an invalid keywords field");
10218
+ }
10219
+ function hashClaudePluginManifest(text2) {
10220
+ const manifest = parse3(text2);
10221
+ validateDescriptiveFields(manifest);
10222
+ const discovery = { kind: "object", entries: manifest.entries.filter(([key]) => !DESCRIPTIVE_FIELDS.has(key)) };
10223
+ return createHash7("sha256").update(canonical3(discovery)).digest("hex");
10224
+ }
10225
+ function projectReviewedClaudePluginManifest(text2, reviewedSha256) {
10226
+ if (!/^[a-f0-9]{64}$/.test(reviewedSha256) || createHash7("sha256").update(text2).digest("hex") !== reviewedSha256) {
10227
+ throw new Error("Reviewed Claude plugin manifest changed before semantic projection");
10228
+ }
10229
+ return hashClaudePluginManifest(text2);
10230
+ }
10231
+
10095
10232
  // src/lib/plugin-discovery.ts
10096
10233
  import { closeSync as closeSync12, constants as constants12, fstatSync as fstatSync12, lstatSync as lstatSync22, openSync as openSync12, opendirSync as opendirSync5, readSync as readSync10 } from "fs";
10097
- import { basename as basename8, dirname as dirname17, isAbsolute as isAbsolute16, join as join30, resolve as resolve16 } from "path";
10234
+ import { basename as basename8, dirname as dirname17, isAbsolute as isAbsolute16, join as join31, resolve as resolve16 } from "path";
10098
10235
 
10099
10236
  // src/lib/plugin-admission.ts
10100
10237
  import { homedir as homedir5 } from "os";
10101
- import { isAbsolute as isAbsolute15, join as join29, resolve as resolve15 } from "path";
10238
+ import { isAbsolute as isAbsolute15, join as join30, resolve as resolve15 } from "path";
10102
10239
 
10103
10240
  // src/lib/selection-cache.ts
10104
- import { createHash as createHash8, randomUUID } from "crypto";
10241
+ import { createHash as createHash9, randomUUID } from "crypto";
10105
10242
  import { spawnSync } from "child_process";
10106
10243
  import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, fsyncSync, linkSync, lstatSync as lstatSync8, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync8, readlinkSync as readlinkSync2, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
10107
10244
  import { hostname } from "os";
10108
- import { dirname as dirname8, join as join11, parse as parse3, resolve as resolve8 } from "path";
10245
+ import { dirname as dirname8, join as join12, parse as parse4, resolve as resolve8 } from "path";
10109
10246
  import { isDeepStrictEqual } from "util";
10110
10247
 
10111
10248
  // src/lib/skill-bundle.ts
10112
- import { createHash as createHash7 } from "crypto";
10249
+ import { createHash as createHash8 } from "crypto";
10113
10250
  import { readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
10114
- import { join as join10, relative as relative2 } from "path";
10251
+ import { join as join11, relative as relative2 } from "path";
10115
10252
  import { createGunzip } from "zlib";
10116
10253
  var BLOCK = 512;
10117
10254
  var ANY_SEGMENT_EXCLUDES = new Set([
@@ -10264,7 +10401,7 @@ function ownBytes(view) {
10264
10401
  return out;
10265
10402
  }
10266
10403
  function sha256Hex(bytes) {
10267
- return createHash7("sha256").update(bytes).digest("hex");
10404
+ return createHash8("sha256").update(bytes).digest("hex");
10268
10405
  }
10269
10406
  function collectSkillBundleEntries(dir) {
10270
10407
  const entries = [];
@@ -10273,7 +10410,7 @@ function collectSkillBundleEntries(dir) {
10273
10410
  }
10274
10411
  function walk(root, current, out) {
10275
10412
  for (const entry of readdirSync4(current, { withFileTypes: true })) {
10276
- const absolute = join10(current, entry.name);
10413
+ const absolute = join11(current, entry.name);
10277
10414
  const rel = relative2(root, absolute).split("\\").join("/");
10278
10415
  const isRootLevel = !rel.includes("/");
10279
10416
  if (ANY_SEGMENT_EXCLUDES.has(entry.name.toLowerCase()))
@@ -10748,17 +10885,17 @@ class SkillSelectionError extends Error {
10748
10885
  var MAX_CACHED_PROFILE_AGE_MS = 24 * 60 * 60 * 1000;
10749
10886
  var SELECTION_LOCK_FILE = "selection.lock.json";
10750
10887
  function selectionCacheRoot(options = {}) {
10751
- return resolve8(options.cacheDir ?? join11(getDataDirReadOnly(), "selection-cache"));
10888
+ return resolve8(options.cacheDir ?? join12(getDataDirReadOnly(), "selection-cache"));
10752
10889
  }
10753
10890
  function selectionKey(selection) {
10754
10891
  validateSelection(selection);
10755
10892
  return hash([selection.authority, selection.workspaceId, selection.slug, selection.version, selection.bundleDigest]);
10756
10893
  }
10757
10894
  function hash(value) {
10758
- return createHash8("sha256").update(JSON.stringify(value)).digest("hex");
10895
+ return createHash9("sha256").update(JSON.stringify(value)).digest("hex");
10759
10896
  }
10760
10897
  function validateSelection(selection) {
10761
- if (!selection || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(selection.slug) || !isValidSkillVersion(selection.version) || !/^sha256:[a-f0-9]{64}$/.test(selection.bundleDigest) || typeof selection.workspaceId !== "string" || !selection.workspaceId.trim() || typeof selection.profileRevision !== "string" || !selection.profileRevision.trim()) {
10898
+ if (!selection || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(selection.slug) || !isValidSkillVersion(selection.version) || !/^sha256:[a-f0-9]{64}$/.test(selection.bundleDigest) || typeof selection.workspaceId !== "string" || !selection.workspaceId.trim() || selection.authorizationEpoch !== undefined && (typeof selection.authorizationEpoch !== "string" || !/^[a-f0-9]{32}$/.test(selection.authorizationEpoch)) || typeof selection.profileRevision !== "string" || !selection.profileRevision.trim()) {
10762
10899
  throw new SkillSelectionError("INVALID_SELECTION", "A skill selection must carry an exact version, digest, workspace and profile revision.");
10763
10900
  }
10764
10901
  const aliasError = selectionAliasError([selection]);
@@ -10792,15 +10929,15 @@ function validateResolvedProfile(profile, authority) {
10792
10929
  }
10793
10930
  function selectionBundlePath(selection, options = {}) {
10794
10931
  validateSelection(selection);
10795
- return join11(selectionCacheRoot(options), "objects", hash(selection.authority), hash(selection.workspaceId), `${selection.bundleDigest.slice(7)}.tar.gz`);
10932
+ return join12(selectionCacheRoot(options), "objects", hash(selection.authority), hash(selection.workspaceId), `${selection.bundleDigest.slice(7)}.tar.gz`);
10796
10933
  }
10797
10934
  function assertRegularPath(path, createParents = false) {
10798
10935
  const absolute = resolve8(path);
10799
- const root = parse3(absolute).root;
10936
+ const root = parse4(absolute).root;
10800
10937
  let cursor = root;
10801
10938
  const segments = absolute.slice(root.length).split("/");
10802
10939
  for (let index = 0;index < segments.length; index++) {
10803
- cursor = join11(cursor, segments[index]);
10940
+ cursor = join12(cursor, segments[index]);
10804
10941
  try {
10805
10942
  const stat = lstatSync8(cursor);
10806
10943
  if (stat.isSymbolicLink() || index < segments.length - 1 && !stat.isDirectory()) {
@@ -10852,7 +10989,7 @@ function writeSelectionJson(path, value) {
10852
10989
  }
10853
10990
  function atomicWrite(path, bytes, mode, durable = false) {
10854
10991
  assertRegularPath(path, true);
10855
- const temporary = join11(dirname8(path), `.selection-${randomUUID()}.tmp`);
10992
+ const temporary = join12(dirname8(path), `.selection-${randomUUID()}.tmp`);
10856
10993
  try {
10857
10994
  const fd = openSync3(temporary, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, mode);
10858
10995
  try {
@@ -10884,17 +11021,21 @@ function readSelectionJson(path) {
10884
11021
  throw new SkillSelectionError("INVALID_RECEIPT", "The Skills selection receipt is unreadable; sync the profile again.");
10885
11022
  }
10886
11023
  }
10887
- async function verifiedEntries(selection, bytes) {
11024
+ async function verifiedEntries(selection, bytes, signal, deadline) {
10888
11025
  if (`sha256:${sha256Hex(bytes)}` !== selection.bundleDigest)
10889
11026
  throw new SkillSelectionError("BUNDLE_DIGEST_MISMATCH", "The skill bundle does not match its selected digest.");
10890
- const bundle = await inspectSkillBundle(bytes);
11027
+ const remaining = deadline === undefined ? undefined : Math.floor(deadline - performance.now());
11028
+ signal?.throwIfAborted();
11029
+ if (remaining !== undefined && remaining <= 0)
11030
+ throw new SkillSelectionError("SKILLS_API_UNAVAILABLE", "Skills session authorization deadline exceeded.");
11031
+ const bundle = await inspectSkillBundle(bytes, { signal, ...remaining === undefined ? {} : { limits: { timeoutMs: remaining } } });
10891
11032
  return bundle.entries;
10892
11033
  }
10893
11034
  async function readCachedSelection(selection, options = {}) {
10894
11035
  const bytes = readRegularFile(selectionBundlePath(selection, options), SKILL_BUNDLE_INSPECTION_LIMITS.compressedBytes);
10895
11036
  return bytes ? verifiedEntries(selection, bytes) : null;
10896
11037
  }
10897
- async function cacheSelectionBundle(selection, response, options = {}) {
11038
+ async function verifySelectionBundleResponse(selection, response, signal, deadline) {
10898
11039
  validateSelection(selection);
10899
11040
  if (!response?.ok)
10900
11041
  throw new SkillSelectionError("BUNDLE_UNAVAILABLE", `The selected skill bundle is unavailable${response ? ` (HTTP ${response.status})` : ""}.`);
@@ -10904,15 +11045,27 @@ async function cacheSelectionBundle(selection, response, options = {}) {
10904
11045
  await response.body?.cancel();
10905
11046
  throw new SkillSelectionError("BUNDLE_IDENTITY_MISMATCH", "The returned bundle does not match the selected version and digest.");
10906
11047
  }
11048
+ const epoch = response.headers.get("X-Skill-Authorization-Epoch");
11049
+ if (selection.authorizationEpoch !== undefined && epoch !== selection.authorizationEpoch) {
11050
+ response.body?.cancel().catch(() => {});
11051
+ throw new SkillSelectionError("SESSION_RECONCILIATION_REQUIRED", "The selected bundle lifecycle authorization changed or is unavailable.");
11052
+ }
10907
11053
  const maximum = SKILL_BUNDLE_INSPECTION_LIMITS.compressedBytes;
10908
11054
  const reader = response.body?.getReader();
10909
11055
  if (!reader)
10910
11056
  throw new SkillSelectionError("BUNDLE_UNAVAILABLE", "The selected bundle response has no body.");
11057
+ signal?.throwIfAborted();
11058
+ const abort = () => {
11059
+ reader.cancel().catch(() => {});
11060
+ };
11061
+ signal?.addEventListener("abort", abort, { once: true });
10911
11062
  const chunks = [];
10912
11063
  let length = 0;
10913
11064
  try {
10914
11065
  while (true) {
11066
+ signal?.throwIfAborted();
10915
11067
  const next = await reader.read();
11068
+ signal?.throwIfAborted();
10916
11069
  if (next.done)
10917
11070
  break;
10918
11071
  length += next.value.byteLength;
@@ -10921,9 +11074,10 @@ async function cacheSelectionBundle(selection, response, options = {}) {
10921
11074
  chunks.push(next.value);
10922
11075
  }
10923
11076
  } catch (error) {
10924
- await reader.cancel().catch(() => {});
11077
+ reader.cancel().catch(() => {});
10925
11078
  throw error;
10926
11079
  } finally {
11080
+ signal?.removeEventListener("abort", abort);
10927
11081
  reader.releaseLock();
10928
11082
  }
10929
11083
  const bytes = new Uint8Array(length);
@@ -10932,7 +11086,12 @@ async function cacheSelectionBundle(selection, response, options = {}) {
10932
11086
  bytes.set(chunk, offset);
10933
11087
  offset += chunk.byteLength;
10934
11088
  }
10935
- const entries = await verifiedEntries(selection, bytes);
11089
+ const entries = await verifiedEntries(selection, bytes, signal, deadline);
11090
+ return { bytes, entries };
11091
+ }
11092
+ async function cacheSelectionBundle(selection, response, options = {}) {
11093
+ const { bytes, entries } = await verifySelectionBundleResponse(selection, response);
11094
+ const maximum = SKILL_BUNDLE_INSPECTION_LIMITS.compressedBytes;
10936
11095
  const path = selectionBundlePath(selection, options);
10937
11096
  const existing = readRegularFile(path, maximum);
10938
11097
  if (existing)
@@ -10942,7 +11101,7 @@ async function cacheSelectionBundle(selection, response, options = {}) {
10942
11101
  return entries;
10943
11102
  }
10944
11103
  function profilePath(profileId, options) {
10945
- return join11(selectionCacheRoot(options), "profiles", `${hash(profileId)}.json`);
11104
+ return join12(selectionCacheRoot(options), "profiles", `${hash(profileId)}.json`);
10946
11105
  }
10947
11106
  function activateSelectionProfile(profile, options = {}) {
10948
11107
  validateResolvedProfile(profile);
@@ -10974,7 +11133,7 @@ function assertFreshCachedProfile(receipt, options = {}) {
10974
11133
  }
10975
11134
  }
10976
11135
  function projectSelectionLockPath(projectDir) {
10977
- return join11(resolve8(projectDir), ".skills", SELECTION_LOCK_FILE);
11136
+ return join12(resolve8(projectDir), ".skills", SELECTION_LOCK_FILE);
10978
11137
  }
10979
11138
  function readProjectSelection(projectDir) {
10980
11139
  const receipt = readSelectionJson(projectSelectionLockPath(projectDir));
@@ -10985,7 +11144,7 @@ function readProjectSelection(projectDir) {
10985
11144
  function sessionReceiptPath(sessionId, options = {}) {
10986
11145
  if (!sessionId.trim() || sessionId.length > MAX_SKILL_SESSION_ID_CHARS)
10987
11146
  throw new SkillSelectionError("INVALID_SESSION", `A Skills session id must contain 1\u2013${MAX_SKILL_SESSION_ID_CHARS} characters.`);
10988
- return join11(selectionCacheRoot(options), "sessions", `${hash(sessionId)}.json`);
11147
+ return join12(selectionCacheRoot(options), "sessions", `${hash(sessionId)}.json`);
10989
11148
  }
10990
11149
  function skillSessionGeneration(receipt) {
10991
11150
  return receipt.generation ?? 0;
@@ -11191,9 +11350,9 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
11191
11350
  const currentReceipt = readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions);
11192
11351
  if (currentReceipt?.sha256 !== receipt?.sha256 || currentReceipt?.generation !== receipt?.generation)
11193
11352
  return null;
11194
- const directory = join11(dirname8(dirname8(lock.path)), "session-lock-recoveries", randomUUID());
11195
- const archivePath = join11(directory, "original.write-lock");
11196
- const recordPath = join11(directory, "recovery.json");
11353
+ const directory = join12(dirname8(dirname8(lock.path)), "session-lock-recoveries", randomUUID());
11354
+ const archivePath = join12(directory, "original.write-lock");
11355
+ const recordPath = join12(directory, "recovery.json");
11197
11356
  assertRegularPath(archivePath, true);
11198
11357
  archiveBytes(recordPath, Buffer.from(`${JSON.stringify({
11199
11358
  schemaVersion: 1,
@@ -11218,7 +11377,7 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
11218
11377
  moved = true;
11219
11378
  const archivedPaths = [archivePath];
11220
11379
  if (finalLock.stagingPath) {
11221
- const publicationPath = join11(directory, "publication.write-lock");
11380
+ const publicationPath = join12(directory, "publication.write-lock");
11222
11381
  renameSync(finalLock.stagingPath, publicationPath);
11223
11382
  archivedPaths.push(publicationPath);
11224
11383
  }
@@ -11384,8 +11543,8 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11384
11543
  if (before.receipt.profile.authority !== replacement.profile.authority || before.receipt.profile.workspaceId !== replacement.profile.workspaceId) {
11385
11544
  throw new SkillSelectionError("PROFILE_IDENTITY_MISMATCH", "A session reconciliation cannot change its Skills authority or workspace.");
11386
11545
  }
11387
- const directory = join11(selectionCacheRoot(options), "session-reconciliations", randomUUID());
11388
- const archivePath = join11(directory, "original.json"), replacementPath = join11(directory, "replacement.json"), receiptPath = join11(directory, "receipt.json");
11546
+ const directory = join12(selectionCacheRoot(options), "session-reconciliations", randomUUID());
11547
+ const archivePath = join12(directory, "original.json"), replacementPath = join12(directory, "replacement.json"), receiptPath = join12(directory, "receipt.json");
11389
11548
  const afterSha256 = sha256Hex(bytes);
11390
11549
  archiveBytes(archivePath, before.bytes);
11391
11550
  archiveBytes(replacementPath, bytes);
@@ -11413,7 +11572,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11413
11572
  // package.json
11414
11573
  var package_default = {
11415
11574
  name: "@hasna/skills",
11416
- version: "0.10.46",
11575
+ version: "0.10.48",
11417
11576
  description: "Skills library for AI coding agents",
11418
11577
  type: "module",
11419
11578
  bin: {
@@ -11555,7 +11714,7 @@ import { createRequire } from "module";
11555
11714
  import { fileURLToPath as fileURLToPath2 } from "url";
11556
11715
  import { closeSync as closeSync4, fstatSync as fstatSync4, lstatSync as lstatSync9, openSync as openSync4, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync6 } from "fs";
11557
11716
  import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
11558
- import { basename as basename4, dirname as dirname9, join as join12, relative as relative3, sep as sep3 } from "path";
11717
+ import { basename as basename4, dirname as dirname9, join as join13, relative as relative3, sep as sep4 } from "path";
11559
11718
  import { fileURLToPath, pathToFileURL } from "url";
11560
11719
  function envToken(name) {
11561
11720
  return name.toUpperCase().replace(/-/g, "_");
@@ -11633,7 +11792,7 @@ var PACKAGE = "@hasna/secrets";
11633
11792
  var MAX_PACKAGE_BYTES = 1024 * 1024;
11634
11793
  function entryPoint(directory) {
11635
11794
  const root = realpathSync2(directory);
11636
- const fd = openSync4(join12(root, "package.json"), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
11795
+ const fd = openSync4(join13(root, "package.json"), O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
11637
11796
  let pkg;
11638
11797
  try {
11639
11798
  const stat = fstatSync4(fd);
@@ -11653,9 +11812,9 @@ function entryPoint(directory) {
11653
11812
  if (/[\\%?#\0]/.test(target) || parts.some((part) => !part || part === "." || part === ".." || part === "node_modules")) {
11654
11813
  throw new Error("Invalid Secrets import export");
11655
11814
  }
11656
- const file = realpathSync2(join12(root, ...parts));
11815
+ const file = realpathSync2(join13(root, ...parts));
11657
11816
  const within = relative3(root, file);
11658
- if (!within || within === ".." || within.startsWith(`..${sep3}`) || !statSync6(file).isFile()) {
11817
+ if (!within || within === ".." || within.startsWith(`..${sep4}`) || !statSync6(file).isFile()) {
11659
11818
  throw new Error("Secrets import export escapes its package or is not a file");
11660
11819
  }
11661
11820
  return pathToFileURL(file).href;
@@ -11664,7 +11823,7 @@ function resolveInstalledSecrets(parentUrl) {
11664
11823
  let directory = dirname9(fileURLToPath(parentUrl));
11665
11824
  for (;; ) {
11666
11825
  if (basename4(directory) !== "node_modules") {
11667
- const candidate = join12(directory, "node_modules", PACKAGE);
11826
+ const candidate = join13(directory, "node_modules", PACKAGE);
11668
11827
  let absent = false;
11669
11828
  try {
11670
11829
  lstatSync9(candidate);
@@ -12811,7 +12970,7 @@ class MissingSkillsFleetError extends Error {
12811
12970
  }
12812
12971
 
12813
12972
  // src/lib/remote-files.ts
12814
- import { createHash as createHash9 } from "crypto";
12973
+ import { createHash as createHash10 } from "crypto";
12815
12974
  var MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
12816
12975
  function describeRemoteFiles(files) {
12817
12976
  if (files.length > 10)
@@ -12829,9 +12988,9 @@ function describeRemoteFiles(files) {
12829
12988
  });
12830
12989
  }
12831
12990
  function sha256(bytes) {
12832
- return createHash9("sha256").update(bytes).digest("hex");
12991
+ return createHash10("sha256").update(bytes).digest("hex");
12833
12992
  }
12834
- async function readBoundedResponse(response, maximum) {
12993
+ async function readBoundedResponse(response, maximum, signal) {
12835
12994
  if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
12836
12995
  throw new Error("Invalid artifact size limit");
12837
12996
  const length = response.headers.get("content-length");
@@ -12842,11 +13001,18 @@ async function readBoundedResponse(response, maximum) {
12842
13001
  const reader = response.body?.getReader();
12843
13002
  if (!reader)
12844
13003
  return new Uint8Array;
13004
+ signal?.throwIfAborted();
13005
+ const abort = () => {
13006
+ reader.cancel().catch(() => {});
13007
+ };
13008
+ signal?.addEventListener("abort", abort, { once: true });
12845
13009
  const chunks = [];
12846
13010
  let size = 0;
12847
13011
  try {
12848
13012
  while (true) {
13013
+ signal?.throwIfAborted();
12849
13014
  const next = await reader.read();
13015
+ signal?.throwIfAborted();
12850
13016
  if (next.done)
12851
13017
  break;
12852
13018
  size += next.value.byteLength;
@@ -12858,6 +13024,7 @@ async function readBoundedResponse(response, maximum) {
12858
13024
  await reader.cancel().catch(() => {});
12859
13025
  throw error;
12860
13026
  } finally {
13027
+ signal?.removeEventListener("abort", abort);
12861
13028
  reader.releaseLock();
12862
13029
  }
12863
13030
  const bytes = new Uint8Array(size);
@@ -12899,10 +13066,12 @@ function validateSelection2(value) {
12899
13066
 
12900
13067
  class HttpProfileClient {
12901
13068
  key;
13069
+ signal;
12902
13070
  authority;
12903
13071
  origin;
12904
- constructor(key, origin) {
13072
+ constructor(key, origin, signal) {
12905
13073
  this.key = key;
13074
+ this.signal = signal;
12906
13075
  if (!key.trim())
12907
13076
  throw new Error("A Skills credential is required");
12908
13077
  this.origin = normalizeSkillsApiOrigin(origin);
@@ -12916,7 +13085,7 @@ class HttpProfileClient {
12916
13085
  ...init,
12917
13086
  redirect: "error",
12918
13087
  credentials: "omit",
12919
- signal: AbortSignal.timeout(15000),
13088
+ signal: this.signal ? AbortSignal.any([this.signal, AbortSignal.timeout(15000)]) : AbortSignal.timeout(15000),
12920
13089
  headers: {
12921
13090
  "Content-Type": "application/json",
12922
13091
  "User-Agent": `hasna-skills/${package_default.version}`,
@@ -12941,22 +13110,28 @@ class HttpProfileClient {
12941
13110
  }
12942
13111
  async read(response, limit = MAX_PROFILE_DOCUMENT_BYTES) {
12943
13112
  try {
12944
- return JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, limit)));
13113
+ return JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, limit, this.signal)));
12945
13114
  } catch {
12946
13115
  invalid();
12947
13116
  }
12948
13117
  }
13118
+ async supportsPinRenewal() {
13119
+ const result = await this.read(await this.request("/capabilities"));
13120
+ if (!object3(result) || !Array.isArray(result.capabilities))
13121
+ invalid();
13122
+ return result.capabilities.includes("skills.session-pin-renewal");
13123
+ }
12949
13124
  async resolveProfile(id) {
12950
13125
  if (!identifier(id))
12951
13126
  throw new Error("Invalid selection profile id");
12952
- const result = await this.read(await this.request(`/profiles/${encodeURIComponent(id)}/resolve`), MAX_RESOLVED_PROFILE_BYTES);
13127
+ const result = await this.read(await this.request(`/profiles/${encodeURIComponent(id)}/resolve?pinAuthorization=epoch-v1`), MAX_RESOLVED_PROFILE_BYTES);
12953
13128
  if (!object3(result) || result.profileId !== id || result.authority !== this.authority || !identifier(result.workspaceId) || !identifier(result.profileRevision) || !Array.isArray(result.selections) || result.selections.length > MAX_PROFILE_SELECTIONS)
12954
13129
  invalid();
12955
13130
  const seen = new Set;
12956
13131
  for (const entry of result.selections) {
12957
13132
  validateSelection2(entry);
12958
13133
  const item = entry;
12959
- if (seen.has(item.slug) || item.authority !== this.authority || item.workspaceId !== result.workspaceId || item.profileRevision !== result.profileRevision)
13134
+ if (item.authorizationEpoch !== undefined && (typeof item.authorizationEpoch !== "string" || !/^[a-f0-9]{32}$/.test(item.authorizationEpoch)) || seen.has(item.slug) || item.authority !== this.authority || item.workspaceId !== result.workspaceId || item.profileRevision !== result.profileRevision)
12960
13135
  invalid();
12961
13136
  seen.add(item.slug);
12962
13137
  }
@@ -13003,7 +13178,7 @@ class HttpProfileClient {
13003
13178
  return this.request(`/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}/bundle`);
13004
13179
  }
13005
13180
  }
13006
- async function createProfileClient() {
13181
+ async function createProfileClient(signal) {
13007
13182
  let connection;
13008
13183
  try {
13009
13184
  connection = await resolveSkillsConnection();
@@ -13015,12 +13190,12 @@ async function createProfileClient() {
13015
13190
  }
13016
13191
  if (!connection)
13017
13192
  throw new Error("Selection profiles require a configured Skills API");
13018
- return new HttpProfileClient(connection.apiKey, connection.apiOrigin);
13193
+ return new HttpProfileClient(connection.apiKey, connection.apiOrigin, signal);
13019
13194
  }
13020
13195
 
13021
13196
  // src/lib/dependency-preparation.ts
13022
13197
  import { constants as constants5, closeSync as closeSync6, fstatSync as fstatSync6, lstatSync as lstatSync11, mkdirSync as mkdirSync4, openSync as openSync6, readFileSync as readFileSync10, readdirSync as readdirSync5, rmdirSync, unlinkSync as unlinkSync2, ftruncateSync, writeSync } from "fs";
13023
- import { join as join13 } from "path";
13198
+ import { join as join14 } from "path";
13024
13199
 
13025
13200
  // src/lib/preparation-process.ts
13026
13201
  function capturePreparationProcess(child, platform = process.platform, signalGroup = (pid, signal) => process.kill(pid, signal)) {
@@ -13067,15 +13242,15 @@ var DEPENDENCY_PREPARATION_MARKER = ".skills-dependency-preparation";
13067
13242
  var RECOVERY = "Skill dependency preparation is incomplete. Confirm no installer is running, successfully run bun install --no-save in the skill directory, then remove only .skills-dependency-preparation. See docs/skill-standard.md#recovering-interrupted-dependency-preparation.";
13068
13243
  function hasDependencyPreparationMarker(skillPath) {
13069
13244
  try {
13070
- lstatSync11(join13(skillPath, DEPENDENCY_PREPARATION_MARKER));
13245
+ lstatSync11(join14(skillPath, DEPENDENCY_PREPARATION_MARKER));
13071
13246
  return true;
13072
13247
  } catch (error) {
13073
13248
  return error.code !== "ENOENT";
13074
13249
  }
13075
13250
  }
13076
13251
  function claimPreparation(skillPath) {
13077
- const marker = join13(skillPath, DEPENDENCY_PREPARATION_MARKER);
13078
- const state = join13(marker, "state.json"), active = join13(marker, "active");
13252
+ const marker = join14(skillPath, DEPENDENCY_PREPARATION_MARKER);
13253
+ const state = join14(marker, "state.json"), active = join14(marker, "active");
13079
13254
  let created = false;
13080
13255
  try {
13081
13256
  mkdirSync4(marker, { mode: 448 });
@@ -13227,14 +13402,14 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
13227
13402
 
13228
13403
  // src/lib/skillinfo.ts
13229
13404
  import { existsSync as existsSync13, readFileSync as readFileSync18 } from "fs";
13230
- import { join as join27 } from "path";
13405
+ import { join as join28 } from "path";
13231
13406
 
13232
13407
  // src/lib/codex-corpus-write.ts
13233
13408
  import { KernelLock } from "@hasna/contracts/kernel-lock";
13234
13409
  import { execFileSync } from "child_process";
13235
- import { createHash as createHash10 } from "crypto";
13410
+ import { createHash as createHash11 } from "crypto";
13236
13411
  import { accessSync, constants as constants6, lstatSync as lstatSync12, readFileSync as readFileSync11, realpathSync as realpathSync3, statSync as statSync7 } from "fs";
13237
- import { basename as basename5, delimiter, dirname as dirname10, isAbsolute as isAbsolute9, join as join14, relative as relative4, resolve as resolve9, sep as sep4 } from "path";
13412
+ import { basename as basename5, delimiter, dirname as dirname10, isAbsolute as isAbsolute9, join as join15, relative as relative4, resolve as resolve9, sep as sep5 } from "path";
13238
13413
  var KEY = ".native-corpus-admission";
13239
13414
  var fail = () => {
13240
13415
  throw new Error("CODEX_CORPUS_ADMISSION_REQUIRED: verified native admission and its existing shared lock are required");
@@ -13243,7 +13418,7 @@ function physicalTarget(path) {
13243
13418
  const suffix = [];
13244
13419
  for (let cursor = path;; cursor = dirname10(cursor)) {
13245
13420
  try {
13246
- return join14(realpathSync3(cursor), ...suffix);
13421
+ return join15(realpathSync3(cursor), ...suffix);
13247
13422
  } catch (error) {
13248
13423
  if (error.code !== "ENOENT" || cursor === dirname10(cursor))
13249
13424
  throw error;
@@ -13264,17 +13439,17 @@ function codexCorpusRootForPath(path) {
13264
13439
  const selectedRoots = selected && isAbsolute9(selected) ? [resolve9(selected), physicalTarget(resolve9(selected))] : [];
13265
13440
  const classify = (candidate) => {
13266
13441
  const checked = (root2) => {
13267
- if (candidate === root2 || candidate === join14(root2, `${KEY}.flock-v1`))
13442
+ if (candidate === root2 || candidate === join15(root2, `${KEY}.flock-v1`))
13268
13443
  fail();
13269
13444
  return root2;
13270
13445
  };
13271
13446
  for (const root2 of selectedRoots) {
13272
13447
  const distance = relative4(root2, candidate);
13273
- if (distance === "" || !distance.startsWith(`..${sep4}`) && distance !== ".." && !isAbsolute9(distance))
13448
+ if (distance === "" || !distance.startsWith(`..${sep5}`) && distance !== ".." && !isAbsolute9(distance))
13274
13449
  return checked(root2);
13275
13450
  }
13276
13451
  for (let cursor = candidate;cursor !== dirname10(cursor); cursor = dirname10(cursor)) {
13277
- if (cursor.endsWith(`${sep4}.codex`))
13452
+ if (cursor.endsWith(`${sep5}.codex`))
13278
13453
  return checked(cursor);
13279
13454
  }
13280
13455
  return;
@@ -13301,8 +13476,8 @@ function executable(options) {
13301
13476
  let found;
13302
13477
  if (isAbsolute9(command))
13303
13478
  found = command;
13304
- else if (!command.includes(sep4)) {
13305
- found = (process.env.PATH ?? "").split(delimiter).filter(isAbsolute9).map((dir) => join14(dir, command)).find((candidate) => {
13479
+ else if (!command.includes(sep5)) {
13480
+ found = (process.env.PATH ?? "").split(delimiter).filter(isAbsolute9).map((dir) => join15(dir, command)).find((candidate) => {
13306
13481
  try {
13307
13482
  accessSync(candidate, constants6.X_OK);
13308
13483
  return true;
@@ -13316,7 +13491,7 @@ function executable(options) {
13316
13491
  const path = realpathSync3(found), initial = statSync7(path, { bigint: true });
13317
13492
  if (!initial.isFile() || (initial.mode & 0o022n) !== 0n || (initial.mode & 0o111n) === 0n || initial.uid !== BigInt(process.getuid()) && initial.uid !== 0n)
13318
13493
  return fail();
13319
- const hash2 = () => createHash10("sha256").update(readFileSync11(path)).digest("hex");
13494
+ const hash2 = () => createHash11("sha256").update(readFileSync11(path)).digest("hex");
13320
13495
  const digest = hash2();
13321
13496
  if (options.codexSha256 && digest !== options.codexSha256)
13322
13497
  return fail();
@@ -13353,7 +13528,7 @@ function acquireCodexCorpusWrite(roots, options) {
13353
13528
  for (const { root } of held) {
13354
13529
  assertCurrent();
13355
13530
  native.assertCurrent();
13356
- const rootIdentity = identity3(root), lockIdentity = identity3(join14(root, `${KEY}.flock-v1`));
13531
+ const rootIdentity = identity3(root), lockIdentity = identity3(join15(root, `${KEY}.flock-v1`));
13357
13532
  assertCurrent();
13358
13533
  const output = execFileSync(native.path, ["corpus-admission-inspect", "--home", root], {
13359
13534
  encoding: "utf8",
@@ -13392,7 +13567,7 @@ function withCodexCorpusWrite(roots, run, options = {}) {
13392
13567
 
13393
13568
  // src/lib/installer.ts
13394
13569
  import { existsSync as existsSync12, readFileSync as readFileSync17, rmSync as rmSync2 } from "fs";
13395
- import { join as join26 } from "path";
13570
+ import { join as join27 } from "path";
13396
13571
  import { homedir as homedir4 } from "os";
13397
13572
 
13398
13573
  // src/lib/agent-sync.ts
@@ -13410,10 +13585,10 @@ import {
13410
13585
  writeFileSync as writeFileSync7
13411
13586
  } from "fs";
13412
13587
  import { homedir as homedir3 } from "os";
13413
- import { basename as basename7, dirname as dirname15, join as join23 } from "path";
13588
+ import { basename as basename7, dirname as dirname15, join as join25 } from "path";
13414
13589
 
13415
13590
  // src/lib/codex-plugin-skill-controls.ts
13416
- import { createHash as createHash11 } from "crypto";
13591
+ import { createHash as createHash12 } from "crypto";
13417
13592
 
13418
13593
  // src/lib/codex-local-mcp-controls.ts
13419
13594
  function isCodexLocalStdioMcpServer(name, server) {
@@ -13482,7 +13657,7 @@ function isCodexLocalStdioMcpServer(name, server) {
13482
13657
  }
13483
13658
 
13484
13659
  // src/lib/codex-plugin-skill-controls.ts
13485
- import { basename as basename6, dirname as dirname11, join as join16, relative as relative5, resolve as resolve10, sep as sep5 } from "path";
13660
+ import { basename as basename6, dirname as dirname11, join as join17, relative as relative5, resolve as resolve10, sep as sep6 } from "path";
13486
13661
  import { lstatSync as lstatSync13, readdirSync as readdirSync6, readlinkSync as readlinkSync3, realpathSync as realpathSync6 } from "fs";
13487
13662
 
13488
13663
  // src/lib/codex-native-compatibility.ts
@@ -13509,7 +13684,7 @@ import { realpathSync as realpathSync5 } from "fs";
13509
13684
  import { isAbsolute as isAbsolute11, normalize as normalize2 } from "path";
13510
13685
 
13511
13686
  // src/lib/codex-hook-rpc.ts
13512
- import { join as join15 } from "path";
13687
+ import { join as join16 } from "path";
13513
13688
  import { execFile, spawn } from "child_process";
13514
13689
  import { realpathSync as realpathSync4, statSync as statSync8 } from "fs";
13515
13690
  import { isAbsolute as isAbsolute10 } from "path";
@@ -13541,7 +13716,7 @@ async function connectCodexHookRpc(options) {
13541
13716
  delete env.CODEX_HOME;
13542
13717
  else
13543
13718
  env.CODEX_HOME = options.codexHome;
13544
- const corpusLease = acquireCodexCorpusWrite([options.codexHome ?? join15(options.home, ".codex")], { codexCommand: binary });
13719
+ const corpusLease = acquireCodexCorpusWrite([options.codexHome ?? join16(options.home, ".codex")], { codexCommand: binary });
13545
13720
  const child = spawn(binary, ["app-server", "--strict-config", "--stdio"], {
13546
13721
  cwd: options.home,
13547
13722
  env,
@@ -13821,7 +13996,7 @@ function remoteInstallationMatches(parent, remotePluginId, read2) {
13821
13996
  try {
13822
13997
  if (!remotePluginIdentifier(remotePluginId) || !lstatSync13(parent).isDirectory() || realpathSync6(parent) !== parent)
13823
13998
  return false;
13824
- const path = join16(parent, ".codex-remote-plugin-install.json"), stat = lstatSync13(path, { throwIfNoEntry: false });
13999
+ const path = join17(parent, ".codex-remote-plugin-install.json"), stat = lstatSync13(path, { throwIfNoEntry: false });
13825
14000
  if (!stat?.isFile() || stat.isSymbolicLink() || stat.size > 16384)
13826
14001
  return false;
13827
14002
  const text2 = read2(path);
@@ -13897,8 +14072,8 @@ function isBundledCleanupHooks(root, namespace, value) {
13897
14072
  return true;
13898
14073
  }
13899
14074
  function rootControls(root, read2) {
13900
- const manifestPath = join16(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync13(manifestPath, { throwIfNoEntry: false });
13901
- if (!lstatSync13(join16(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
14075
+ const manifestPath = join17(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync13(manifestPath, { throwIfNoEntry: false });
14076
+ if (!lstatSync13(join17(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
13902
14077
  refuse2();
13903
14078
  let manifest, manifestSha256;
13904
14079
  try {
@@ -13913,9 +14088,9 @@ function rootControls(root, read2) {
13913
14088
  const cacheVersion = basename6(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
13914
14089
  if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
13915
14090
  refuse2();
13916
- if (manifest.hooks !== undefined && !isBundledCleanupHooks(root, manifest.name, manifest.hooks) || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync13(join16(root, "hooks"), { throwIfNoEntry: false }))
14091
+ if (manifest.hooks !== undefined && !isBundledCleanupHooks(root, manifest.name, manifest.hooks) || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync13(join17(root, "hooks"), { throwIfNoEntry: false }))
13917
14092
  refuse2();
13918
- const mcp = join16(root, ".mcp.json"), mcpPresent = lstatSync13(mcp, { throwIfNoEntry: false });
14093
+ const mcp = join17(root, ".mcp.json"), mcpPresent = lstatSync13(mcp, { throwIfNoEntry: false });
13919
14094
  if (manifest.hooks !== undefined && (manifest.mcpServers !== undefined || mcpPresent))
13920
14095
  refuse2();
13921
14096
  if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync6(mcp) !== mcp))
@@ -13923,7 +14098,7 @@ function rootControls(root, read2) {
13923
14098
  if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
13924
14099
  refuse2();
13925
14100
  const mcpSha256 = mcpPresent ? mcpControls(mcp, read2) : undefined;
13926
- const app = join16(root, ".app.json"), present = lstatSync13(app, { throwIfNoEntry: false });
14101
+ const app = join17(root, ".app.json"), present = lstatSync13(app, { throwIfNoEntry: false });
13927
14102
  if (present && (!present.isFile() || present.isSymbolicLink() || present.size > 1024 * 1024))
13928
14103
  refuse2();
13929
14104
  if (manifest.apps !== undefined && !present)
@@ -13932,7 +14107,7 @@ function rootControls(root, read2) {
13932
14107
  return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
13933
14108
  }
13934
14109
  function documentIdentity(document2, cache, read2) {
13935
- if (!document2.startsWith(cache + sep5) || !document2.endsWith(sep5 + "SKILL.md"))
14110
+ if (!document2.startsWith(cache + sep6) || !document2.endsWith(sep6 + "SKILL.md"))
13936
14111
  refuse2();
13937
14112
  const text2 = read2(document2);
13938
14113
  if (Buffer.byteLength(text2) > 1024 * 1024)
@@ -13955,12 +14130,12 @@ function documentIdentity(document2, cache, read2) {
13955
14130
  }
13956
14131
  if (!front || typeof front !== "object" || !identifier2(front.name) || typeof front.description !== "string" || !front.description.trim())
13957
14132
  refuse2();
13958
- for (let root = dirname11(document2), depth = 0;root.startsWith(cache + sep5) && depth < 12; root = dirname11(root), depth++) {
13959
- const path = join16(root, ".codex-plugin", "plugin.json");
14133
+ for (let root = dirname11(document2), depth = 0;root.startsWith(cache + sep6) && depth < 12; root = dirname11(root), depth++) {
14134
+ const path = join17(root, ".codex-plugin", "plugin.json");
13960
14135
  if (!lstatSync13(path, { throwIfNoEntry: false }))
13961
14136
  continue;
13962
14137
  const manifestFile = lstatSync13(path);
13963
- if (!lstatSync13(join16(root, ".codex-plugin")).isDirectory() || !manifestFile.isFile() || manifestFile.size > 1024 * 1024)
14138
+ if (!lstatSync13(join17(root, ".codex-plugin")).isDirectory() || !manifestFile.isFile() || manifestFile.size > 1024 * 1024)
13964
14139
  refuse2();
13965
14140
  let manifest;
13966
14141
  try {
@@ -13987,13 +14162,13 @@ function identity4(document2, cache, read2) {
13987
14162
  return { name: parsed.name, pluginParent: parsed.pluginParent, ...controls };
13988
14163
  }
13989
14164
  function classifyCodexPluginCacheDocument(document2, cache, read2) {
13990
- if (!document2.startsWith(cache + sep5))
14165
+ if (!document2.startsWith(cache + sep6))
13991
14166
  return null;
13992
14167
  try {
13993
14168
  const located = documentIdentity(document2, cache, read2), parsed = identity4(document2, cache, read2);
13994
- if (!lstatSync13(document2).isFile() || realpathSync6(document2) !== document2 || realpathSync6(located.root) !== located.root || realpathSync6(located.pluginParent) !== located.pluginParent || realpathSync6(join16(located.root, ".codex-plugin/plugin.json")) !== join16(located.root, ".codex-plugin/plugin.json"))
14169
+ if (!lstatSync13(document2).isFile() || realpathSync6(document2) !== document2 || realpathSync6(located.root) !== located.root || realpathSync6(located.pluginParent) !== located.pluginParent || realpathSync6(join17(located.root, ".codex-plugin/plugin.json")) !== join17(located.root, ".codex-plugin/plugin.json"))
13995
14170
  return null;
13996
- const parts = relative5(cache, parsed.pluginParent).split(sep5);
14171
+ const parts = relative5(cache, parsed.pluginParent).split(sep6);
13997
14172
  if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== parsed.namespace)
13998
14173
  return null;
13999
14174
  return { name: parsed.name, namespace: parsed.namespace, pluginId: `${parts[1]}@${parts[0]}`, pluginParent: parsed.pluginParent, root: located.root, manifestSha256: parsed.manifestSha256 };
@@ -14003,7 +14178,7 @@ function classifyCodexPluginCacheDocument(document2, cache, read2) {
14003
14178
  }
14004
14179
  function reviewedDisabledCodexPluginParent(cache, parent, controls, rules) {
14005
14180
  try {
14006
- const parts = relative5(cache, parent).split(sep5), enrolled = controls.filter((control) => control.pluginParent === parent);
14181
+ const parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
14007
14182
  if (resolve10(parent) !== parent || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !enrolled.length || !Array.isArray(rules))
14008
14183
  return false;
14009
14184
  for (let cursor = parent;; cursor = dirname11(cursor)) {
@@ -14013,7 +14188,7 @@ function reviewedDisabledCodexPluginParent(cache, parent, controls, rules) {
14013
14188
  if (dirname11(cursor) === cursor)
14014
14189
  break;
14015
14190
  }
14016
- return enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path.startsWith(parent + sep5))) && rule.enabled !== false));
14191
+ return enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path.startsWith(parent + sep6))) && rule.enabled !== false));
14017
14192
  } catch {
14018
14193
  return false;
14019
14194
  }
@@ -14023,7 +14198,7 @@ function absentDisabledCodexPluginParent(cache, parent, controls, rules) {
14023
14198
  }
14024
14199
  function reviewedRetiredCodexRoot(cache, root, controls, rules, read2, allowAbsent = false) {
14025
14200
  try {
14026
- const parent = dirname11(root), parts = relative5(cache, parent).split(sep5), enrolled = controls.filter((control) => control.pluginParent === parent);
14201
+ const parent = dirname11(root), parts = relative5(cache, parent).split(sep6), enrolled = controls.filter((control) => control.pluginParent === parent);
14027
14202
  if (resolve10(root) !== root || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !basename6(root) || basename6(root) === "latest" || !enrolled.length || !Array.isArray(rules))
14028
14203
  return false;
14029
14204
  for (let cursor = root;; cursor = dirname11(cursor)) {
@@ -14033,14 +14208,14 @@ function reviewedRetiredCodexRoot(cache, root, controls, rules, read2, allowAbse
14033
14208
  if (dirname11(cursor) === cursor)
14034
14209
  break;
14035
14210
  }
14036
- if (!enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path === root || rule.path.startsWith(root + sep5))) && rule.enabled !== false)))
14211
+ if (!enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path === root || rule.path.startsWith(root + sep6))) && rule.enabled !== false)))
14037
14212
  return false;
14038
14213
  if (!lstatSync13(root, { throwIfNoEntry: false }))
14039
14214
  return allowAbsent && (lstatSync13(parent, { throwIfNoEntry: false }) ? enrolled.every((control) => remoteInstallationMatches(parent, control.remotePluginId, read2)) : absentDisabledCodexPluginParent(cache, parent, controls, rules));
14040
14215
  if (!enrolled.every((control) => remoteInstallationMatches(parent, control.remotePluginId, read2)))
14041
14216
  return false;
14042
14217
  const parsed = rootControls(root, read2);
14043
- if (JSON.parse(read2(join16(root, ".codex-plugin/plugin.json"))).hooks !== undefined)
14218
+ if (JSON.parse(read2(join17(root, ".codex-plugin/plugin.json"))).hooks !== undefined)
14044
14219
  return false;
14045
14220
  if (!enrolled.some((control) => control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && !parsed.appSha256 && !parsed.mcpSha256))
14046
14221
  return false;
@@ -14048,7 +14223,7 @@ function reviewedRetiredCodexRoot(cache, root, controls, rules, read2, allowAbse
14048
14223
  const emptySkills = (directory, depth) => depth <= 64 && readdirSync6(directory).every((name) => {
14049
14224
  if (++entries > 4096 || name === "SKILL.md")
14050
14225
  return false;
14051
- const path = join16(directory, name), stat = lstatSync13(path);
14226
+ const path = join17(directory, name), stat = lstatSync13(path);
14052
14227
  return !stat.isSymbolicLink() && (stat.isFile() || stat.isDirectory() && emptySkills(path, depth + 1));
14053
14228
  });
14054
14229
  return emptySkills(root, 0);
@@ -14064,7 +14239,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2, rules)
14064
14239
  for (const parent of new Set(controls.map((control) => control.pluginParent))) {
14065
14240
  if (absentDisabledCodexPluginParent(cache, parent, controls, rules))
14066
14241
  continue;
14067
- if (!parent.startsWith(cache + sep5) || !lstatSync13(parent, { throwIfNoEntry: false })?.isDirectory() || realpathSync6(parent) !== parent)
14242
+ if (!parent.startsWith(cache + sep6) || !lstatSync13(parent, { throwIfNoEntry: false })?.isDirectory() || realpathSync6(parent) !== parent)
14068
14243
  return false;
14069
14244
  for (const control of controls.filter((control2) => control2.pluginParent === parent)) {
14070
14245
  if (control.remotePluginId !== undefined && !remoteInstallationMatches(parent, control.remotePluginId, read2))
@@ -14075,7 +14250,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2, rules)
14075
14250
  if (entries > 4096)
14076
14251
  return false;
14077
14252
  for (const name of names) {
14078
- let root = join16(parent, name), stat = lstatSync13(root);
14253
+ let root = join17(parent, name), stat = lstatSync13(root);
14079
14254
  if (stat.isSymbolicLink()) {
14080
14255
  const target = resolve10(parent, readlinkSync3(root));
14081
14256
  if (name !== "latest" || dirname11(target) !== parent || !lstatSync13(target).isDirectory() || realpathSync6(target) !== target)
@@ -14083,7 +14258,7 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read2, rules)
14083
14258
  root = target;
14084
14259
  stat = lstatSync13(root);
14085
14260
  }
14086
- if (!stat.isDirectory() || ![".codex-plugin/plugin.json", ".app.json", "hooks", ".mcp.json"].some((path) => lstatSync13(join16(root, path), { throwIfNoEntry: false })))
14261
+ if (!stat.isDirectory() || ![".codex-plugin/plugin.json", ".app.json", "hooks", ".mcp.json"].some((path) => lstatSync13(join17(root, path), { throwIfNoEntry: false })))
14087
14262
  continue;
14088
14263
  const parsed = rootControls(root, read2);
14089
14264
  if (!controls.some((control) => control.pluginParent === parent && control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && control.appSha256 === parsed.appSha256 && control.mcpSha256 === parsed.mcpSha256))
@@ -14113,12 +14288,12 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14113
14288
  }
14114
14289
  const candidates = catalog.plugins === undefined ? skills.map((skill) => skill.path).filter((path) => allowed.has(path)) : allowed;
14115
14290
  for (const document2 of candidates) {
14116
- if (!document2.startsWith(cache + sep5))
14291
+ if (!document2.startsWith(cache + sep6))
14117
14292
  continue;
14118
14293
  const located = documentIdentity(document2, cache, read2);
14119
14294
  if (located.name === "skills-cli" || located.name.endsWith(":skills-cli"))
14120
14295
  refuse2();
14121
- const cacheParts = relative5(cache, located.pluginParent).split(sep5);
14296
+ const cacheParts = relative5(cache, located.pluginParent).split(sep6);
14122
14297
  if (cacheParts.length !== 2 || cacheParts.some((part) => !identifier2(part)))
14123
14298
  refuse2();
14124
14299
  const [marketplace, pluginName] = cacheParts;
@@ -14131,8 +14306,8 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14131
14306
  const installed = installedPlugins.filter((plugin) => plugin.id === expectedPluginId && plugin.name === pluginName);
14132
14307
  if (catalog.version === "codex-cli 0.160.0" && installed.length === 1 && installed[0].installed && installed[0].localVersion === basename6(located.root) && installed[0].sourceType === "local" && codexPluginSourceIsConfigControlled(installed[0])) {
14133
14308
  const plugin = installed[0], input = { pluginId: expectedPluginId, namespace: located.namespace, pluginParent: located.pluginParent, sourceRoot: plugin.sourcePath, sourceSha256: plugin.sourceSha256 };
14134
- if (input.sourceRoot !== located.root && !input.sourceRoot.startsWith(cache + sep5) && (lstatSync13(input.sourceRoot, { throwIfNoEntry: false }) || documents.some((path) => path.startsWith(input.sourceRoot + sep5)))) {
14135
- if (skills.some((skill) => skill.path === input.sourceRoot || skill.path.startsWith(input.sourceRoot + sep5)) || !sourceInputUnchanged(input, cache, read2))
14309
+ if (input.sourceRoot !== located.root && !input.sourceRoot.startsWith(cache + sep6) && (lstatSync13(input.sourceRoot, { throwIfNoEntry: false }) || documents.some((path) => path.startsWith(input.sourceRoot + sep6)))) {
14310
+ if (skills.some((skill) => skill.path === input.sourceRoot || skill.path.startsWith(input.sourceRoot + sep6)) || !sourceInputUnchanged(input, cache, read2))
14136
14311
  refuse2();
14137
14312
  sourceInputs.set(expectedPluginId, input);
14138
14313
  }
@@ -14142,7 +14317,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14142
14317
  if (!Array.isArray(rules))
14143
14318
  refuse2();
14144
14319
  const exactPathDeny = rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
14145
- if (!lstatSync13(document2).isFile() || realpathSync6(document2) !== document2 || realpathSync6(located.root) !== located.root || realpathSync6(located.pluginParent) !== located.pluginParent || realpathSync6(join16(located.root, ".codex-plugin/plugin.json")) !== join16(located.root, ".codex-plugin/plugin.json") || !exactPathDeny || rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === located.name && rule?.enabled !== false) || !isCodexNativeSkillDisabled({ name: located.name, path: document2, pluginId: expectedPluginId, enabled: true }, rules))
14320
+ if (!lstatSync13(document2).isFile() || realpathSync6(document2) !== document2 || realpathSync6(located.root) !== located.root || realpathSync6(located.pluginParent) !== located.pluginParent || realpathSync6(join17(located.root, ".codex-plugin/plugin.json")) !== join17(located.root, ".codex-plugin/plugin.json") || !exactPathDeny || rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === located.name && rule?.enabled !== false) || !isCodexNativeSkillDisabled({ name: located.name, path: document2, pluginId: expectedPluginId, enabled: true }, rules))
14146
14321
  refuse2();
14147
14322
  inertDocuments.add(document2);
14148
14323
  const plugin = installed[0];
@@ -14171,7 +14346,7 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14171
14346
  const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document2 && rule?.enabled === false);
14172
14347
  const nativeSkill = { name: parsed.name, path: document2, pluginId: expectedPluginId, enabled: true };
14173
14348
  const denial = denials.find((item) => item.path === document2);
14174
- const explicitDeny = denial !== undefined && remotePluginId !== undefined && catalog.version === "codex-cli 0.160.0" && denial.name === parsed.name && denial.sha256 === createHash11("sha256").update(read2(document2)).digest("hex") && Array.isArray(rules) && !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document2 && rule.enabled === false);
14349
+ const explicitDeny = denial !== undefined && remotePluginId !== undefined && catalog.version === "codex-cli 0.160.0" && denial.name === parsed.name && denial.sha256 === createHash12("sha256").update(read2(document2)).digest("hex") && Array.isArray(rules) && !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule.enabled !== false) && Array.isArray(plannedRules) && plannedRules.some((rule) => rule?.path === document2 && rule.enabled === false);
14175
14350
  if (denial && !explicitDeny)
14176
14351
  refuse2();
14177
14352
  if (explicitDeny)
@@ -14180,21 +14355,21 @@ function reviewCodexPluginControls(catalog, documents, cache, cwd, read2, rules,
14180
14355
  if (!explicitDeny && (!exactPathDeny && !plannedPathDeny || !isCodexNativeSkillDisabled(nativeSkill, rules)))
14181
14356
  refuse2();
14182
14357
  }
14183
- const skillsOnly = !parsed.appSha256 && !parsed.mcpSha256 && JSON.parse(read2(join16(located.root, ".codex-plugin/plugin.json"))).hooks === undefined;
14358
+ const skillsOnly = !parsed.appSha256 && !parsed.mcpSha256 && JSON.parse(read2(join17(located.root, ".codex-plugin/plugin.json"))).hooks === undefined;
14184
14359
  result.push({ ...parsed, pluginId: expectedPluginId, ...remotePluginId ? { remotePluginId } : {}, ...skillsOnly ? { skillsOnly: true } : {} });
14185
14360
  }
14186
14361
  if (usedDenials.size !== denials.length)
14187
14362
  refuse2();
14188
- if (!result.length && documents.some((document2) => document2.startsWith(cache + sep5) && !inertDocuments.has(document2)) || new Set(result.map((item) => item.name)).size !== result.length)
14363
+ if (!result.length && documents.some((document2) => document2.startsWith(cache + sep6) && !inertDocuments.has(document2)) || new Set(result.map((item) => item.name)).size !== result.length)
14189
14364
  refuse2();
14190
14365
  return { skills: result, inactivePlugins: [...inactivePlugins.values()], sourceInputs: [...sourceInputs.values()] };
14191
14366
  }
14192
14367
  function sourceInputUnchanged(input, cache, read2) {
14193
14368
  try {
14194
- const parts = relative5(cache, input.pluginParent).split(sep5);
14195
- if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== input.namespace || input.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ id: input.pluginId, sourceType: "local", sourceSha256: input.sourceSha256 }) || input.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: input.sourceRoot })) || resolve10(input.sourceRoot) !== input.sourceRoot || !lstatSync13(input.sourceRoot).isDirectory() || realpathSync6(input.sourceRoot) !== input.sourceRoot || input.sourceRoot === cache || input.sourceRoot.startsWith(cache + sep5) || cache.startsWith(input.sourceRoot + sep5))
14369
+ const parts = relative5(cache, input.pluginParent).split(sep6);
14370
+ if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== input.namespace || input.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ id: input.pluginId, sourceType: "local", sourceSha256: input.sourceSha256 }) || input.sourceSha256 !== hashNativeJsonControls(JSON.stringify({ type: "local", path: input.sourceRoot })) || resolve10(input.sourceRoot) !== input.sourceRoot || !lstatSync13(input.sourceRoot).isDirectory() || realpathSync6(input.sourceRoot) !== input.sourceRoot || input.sourceRoot === cache || input.sourceRoot.startsWith(cache + sep6) || cache.startsWith(input.sourceRoot + sep6))
14196
14371
  return false;
14197
- const path = join16(input.sourceRoot, ".codex-plugin/plugin.json"), stat = lstatSync13(path);
14372
+ const path = join17(input.sourceRoot, ".codex-plugin/plugin.json"), stat = lstatSync13(path);
14198
14373
  if (!stat.isFile() || stat.size > 1024 * 1024 || realpathSync6(path) !== path)
14199
14374
  return false;
14200
14375
  const text2 = read2(path);
@@ -14209,7 +14384,7 @@ function reviewedCodexPluginSourceRoots(cache, inputs, read2) {
14209
14384
  if (!Array.isArray(inputs) || inputs.length > 4096 || new Set(inputs.map((input) => input.pluginId)).size !== inputs.length || new Set(inputs.map((input) => input.sourceRoot)).size !== inputs.length || inputs.some((input) => !sourceInputUnchanged(input, cache, read2)))
14210
14385
  refuse2();
14211
14386
  const roots = inputs.map((input) => input.sourceRoot);
14212
- if (roots.some((root) => roots.some((other) => root !== other && root.startsWith(other + sep5))))
14387
+ if (roots.some((root) => roots.some((other) => root !== other && root.startsWith(other + sep6))))
14213
14388
  refuse2();
14214
14389
  return roots;
14215
14390
  }
@@ -14218,9 +14393,9 @@ function isReviewedCodexPluginInactive(document2, cache, controls, pluginSetting
14218
14393
  return false;
14219
14394
  try {
14220
14395
  const parsed = documentIdentity(document2, cache, read2), matches = controls.filter((control2) => control2.pluginParent === parsed.pluginParent && control2.namespace === parsed.namespace);
14221
- if (matches.length !== 1 || !lstatSync13(document2).isFile() || realpathSync6(document2) !== document2 || realpathSync6(parsed.root) !== parsed.root || realpathSync6(parsed.pluginParent) !== parsed.pluginParent || realpathSync6(join16(parsed.root, ".codex-plugin/plugin.json")) !== join16(parsed.root, ".codex-plugin/plugin.json"))
14396
+ if (matches.length !== 1 || !lstatSync13(document2).isFile() || realpathSync6(document2) !== document2 || realpathSync6(parsed.root) !== parsed.root || realpathSync6(parsed.pluginParent) !== parsed.pluginParent || realpathSync6(join17(parsed.root, ".codex-plugin/plugin.json")) !== join17(parsed.root, ".codex-plugin/plugin.json"))
14222
14397
  return false;
14223
- const control = matches[0], parts = relative5(cache, parsed.pluginParent).split(sep5);
14398
+ const control = matches[0], parts = relative5(cache, parsed.pluginParent).split(sep6);
14224
14399
  if (parts.length !== 2 || parts.some((part) => !identifier2(part)) || parts[1] !== parsed.namespace || control.pluginId !== `${parts[1]}@${parts[0]}` || !codexPluginSourceIsConfigControlled({ ...control, id: control.pluginId }) || !Object.hasOwn(pluginSettings, control.pluginId) || pluginSettings[control.pluginId]?.enabled !== false)
14225
14400
  return false;
14226
14401
  return !rules.some((rule) => (rule?.path === document2 || typeof rule?.name === "string" && rule.name.trim() === parsed.name) && rule?.enabled !== false);
@@ -14229,7 +14404,7 @@ function isReviewedCodexPluginInactive(document2, cache, controls, pluginSetting
14229
14404
  }
14230
14405
  }
14231
14406
  function isReviewedCodexPluginSkillDisabled(document2, cache, controls, rules, read2) {
14232
- if (!document2.startsWith(cache + sep5) || !Array.isArray(controls) || controls.length > 4096)
14407
+ if (!document2.startsWith(cache + sep6) || !Array.isArray(controls) || controls.length > 4096)
14233
14408
  return false;
14234
14409
  let parsed;
14235
14410
  try {
@@ -14258,9 +14433,9 @@ enabled = false
14258
14433
  }
14259
14434
 
14260
14435
  // src/lib/codex-native-skill-policy.ts
14261
- import { createHash as createHash12, randomUUID as randomUUID2 } from "crypto";
14436
+ import { createHash as createHash13, randomUUID as randomUUID2 } from "crypto";
14262
14437
  import { closeSync as closeSync7, constants as constants7, fstatSync as fstatSync7, fsyncSync as fsyncSync2, lstatSync as lstatSync14, mkdirSync as mkdirSync5, openSync as openSync7, readFileSync as readFileSync12, readSync as readSync5, readlinkSync as readlinkSync4, realpathSync as realpathSync7, renameSync as renameSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
14263
- import { dirname as dirname12, isAbsolute as isAbsolute12, join as join17, resolve as resolve11 } from "path";
14438
+ import { dirname as dirname12, isAbsolute as isAbsolute12, join as join18, resolve as resolve11 } from "path";
14264
14439
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
14265
14440
  var CODEX_NATIVE_POLICY_CAPABILITY = "host-path-allowlist-v1";
14266
14441
  var CODEX_NATIVE_POLICY_RECEIPT_SCHEMA = "skills.codex-native-policy-acceptance/v1";
@@ -14386,7 +14561,7 @@ function verifyCodexNativeBridgeDocument(path, expectedContent, expectedSha256)
14386
14561
  if (realpathSync7.native(path) !== path)
14387
14562
  refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document real path differs from its lexical path");
14388
14563
  const bytes = readBounded(path, MAX_BRIDGE_DOCUMENT_BYTES, "the bridge document");
14389
- const sha2562 = createHash12("sha256").update(bytes).digest("hex");
14564
+ const sha2562 = createHash13("sha256").update(bytes).digest("hex");
14390
14565
  if (!bytes.equals(Buffer.from(expectedContent, "utf8")) || sha2562 !== expectedSha256)
14391
14566
  refuse3("NATIVE_SKILL_POLICY_BRIDGE_UNVERIFIED", "the bridge document bytes differ from the managed bridge");
14392
14567
  return { path, sha256: sha2562 };
@@ -14469,7 +14644,7 @@ function executableWitness(path) {
14469
14644
  var witnessKey = (path, witness) => JSON.stringify([path, witness.dev, witness.ino, witness.size, witness.mtimeNs, witness.ctimeNs, witness.uid]);
14470
14645
  var executableDigestMemory = new Map;
14471
14646
  function executableCachePath(dataDir2) {
14472
- return join17(resolve11(dataDir2), "agent-hooks", "codex-native-policy-executable-cache.json");
14647
+ return join18(resolve11(dataDir2), "agent-hooks", "codex-native-policy-executable-cache.json");
14473
14648
  }
14474
14649
  var currentUid = () => process.getuid?.() ?? -1;
14475
14650
  function readTrustedCacheBytes(path, ownerUid) {
@@ -14632,7 +14807,7 @@ function assertOperatorTrustRoot(dataDir2) {
14632
14807
  };
14633
14808
  checkDirectory(resolve11(dataDir2), "the Skills data directory", true);
14634
14809
  checkDirectory(dirname12(executableCachePath(dataDir2)), "the agent-hooks directory", false);
14635
- check(join17(resolve11(dataDir2), "agent-policy.json"), "the managed policy", true);
14810
+ check(join18(resolve11(dataDir2), "agent-policy.json"), "the managed policy", true);
14636
14811
  check(executableCachePath(dataDir2), "the executable identity cache", false);
14637
14812
  }
14638
14813
  function runCodexNativePolicyHelper(request) {
@@ -14803,7 +14978,7 @@ function defaultProcessInspector() {
14803
14978
  return { platform: process.platform, arch: process.arch, pid: process.pid, parentOf: () => null, startTime: () => null, executablePath: () => null };
14804
14979
  }
14805
14980
  function codexNativePolicyReceiptPath(dataDir2) {
14806
- return join17(resolve11(dataDir2), "agent-hooks", "codex-native-policy-acceptance.json");
14981
+ return join18(resolve11(dataDir2), "agent-hooks", "codex-native-policy-acceptance.json");
14807
14982
  }
14808
14983
  function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
14809
14984
  const path = codexNativePolicyReceiptPath(dataDir2), directory = dirname12(path);
@@ -14854,8 +15029,8 @@ function recordCodexNativePolicyAcceptance(dataDir2, verification, accepted) {
14854
15029
  }
14855
15030
 
14856
15031
  // src/lib/codex-settings-witness-v1.ts
14857
- import { createHash as createHash13 } from "crypto";
14858
- var sha = (text2) => createHash13("sha256").update(text2).digest("hex");
15032
+ import { createHash as createHash14 } from "crypto";
15033
+ var sha = (text2) => createHash14("sha256").update(text2).digest("hex");
14859
15034
  var EFFORT = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
14860
15035
  var VERBOSITY = new Set(["low", "medium", "high"]);
14861
15036
  function need3(value) {
@@ -14901,7 +15076,7 @@ function numericTokens(text2) {
14901
15076
  }
14902
15077
  return result;
14903
15078
  }
14904
- function canonical3(value, depth = 0, budget2 = { remaining: 65536 }) {
15079
+ function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
14905
15080
  need3(depth <= 32 && --budget2.remaining >= 0);
14906
15081
  if (value === null)
14907
15082
  return ["null"];
@@ -14918,9 +15093,9 @@ function canonical3(value, depth = 0, budget2 = { remaining: 65536 }) {
14918
15093
  return ["date", value.toISOString()];
14919
15094
  }
14920
15095
  if (Array.isArray(value))
14921
- return ["array", value.map((child) => canonical3(child, depth + 1, budget2))];
15096
+ return ["array", value.map((child) => canonical4(child, depth + 1, budget2))];
14922
15097
  need3(value && typeof value === "object");
14923
- return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical3(child, depth + 1, budget2)])];
15098
+ return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1, budget2)])];
14924
15099
  }
14925
15100
  function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
14926
15101
  need3(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
@@ -14941,7 +15116,7 @@ function hashCodexSettingsReplacement(text2, budget2 = { remaining: 256 * 1024 *
14941
15116
  need3(typeof config[key] === "string" && values.has(config[key]));
14942
15117
  delete config[key];
14943
15118
  }
14944
- return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical3(config), numericTokens(text2)])}`);
15119
+ return sha(`hasna.skills.codex-settings.v1\x00${JSON.stringify([canonical4(config), numericTokens(text2)])}`);
14945
15120
  }
14946
15121
  function captureCodexSettings(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
14947
15122
  const text2 = readNativeSettingsWitnessFile(path, budget2, "config.toml");
@@ -15012,9 +15187,9 @@ function projectRegularCodexMcp(config, source) {
15012
15187
  }
15013
15188
 
15014
15189
  // src/lib/codex-settings-witness.ts
15015
- import { createHash as createHash14 } from "crypto";
15190
+ import { createHash as createHash15 } from "crypto";
15016
15191
  import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
15017
- var sha2 = (text2) => createHash14("sha256").update(text2).digest("hex");
15192
+ var sha2 = (text2) => createHash15("sha256").update(text2).digest("hex");
15018
15193
  var EFFORT2 = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
15019
15194
  var VERBOSITY2 = new Set(["low", "medium", "high"]);
15020
15195
  var MODEL = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
@@ -15069,7 +15244,7 @@ function numericTokens2(text2) {
15069
15244
  }
15070
15245
  return result.sort();
15071
15246
  }
15072
- function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
15247
+ function canonical5(value, depth = 0, budget2 = { remaining: 65536 }) {
15073
15248
  need4(depth <= 32 && --budget2.remaining >= 0);
15074
15249
  if (value === null)
15075
15250
  return ["null"];
@@ -15086,9 +15261,9 @@ function canonical4(value, depth = 0, budget2 = { remaining: 65536 }) {
15086
15261
  return ["date", value.toISOString()];
15087
15262
  }
15088
15263
  if (Array.isArray(value))
15089
- return ["array", value.map((child) => canonical4(child, depth + 1, budget2))];
15264
+ return ["array", value.map((child) => canonical5(child, depth + 1, budget2))];
15090
15265
  need4(isRecord3(value));
15091
- return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical4(child, depth + 1, budget2)])];
15266
+ return ["object", Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, child]) => [key, canonical5(child, depth + 1, budget2)])];
15092
15267
  }
15093
15268
  function normalize3(config, version = 2) {
15094
15269
  if (isRecord3(config.hooks)) {
@@ -15262,7 +15437,7 @@ function settingsDigest2(text2, version = 2) {
15262
15437
  const mcp = version >= 3 ? projectRegularCodexMcp(config, text2) : { config, numericSource: text2 };
15263
15438
  const projected = version === 4 ? projectModelAvailabilityNux(mcp.config, mcp.numericSource) : mcp;
15264
15439
  normalize3(projected.config, version);
15265
- return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify([canonical4(projected.config), numericTokens2(projected.numericSource)])}`);
15440
+ return sha2(`hasna.skills.codex-settings.v${version}\x00${JSON.stringify([canonical5(projected.config), numericTokens2(projected.numericSource)])}`);
15266
15441
  }
15267
15442
  function hashCodexSettingsReplacementV2(text2, budget2 = { remaining: 256 * 1024 * 1024 }) {
15268
15443
  need4(Buffer.byteLength(text2) <= 1024 * 1024 && budget2.remaining >= Buffer.byteLength(text2));
@@ -15326,15 +15501,15 @@ function upgradeCodexSettingsWitnessV4(previous, reviewedSettingsPath) {
15326
15501
  }
15327
15502
 
15328
15503
  // src/lib/agent-integration.ts
15329
- import { createHash as createHash17, randomUUID as randomUUID3 } from "crypto";
15504
+ import { createHash as createHash18, randomUUID as randomUUID3 } from "crypto";
15330
15505
  import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
15331
15506
  import { existsSync as existsSync9, lstatSync as lstatSync19, statSync as statSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync14, readdirSync as readdirSync8, opendirSync as opendirSync3, readlinkSync as readlinkSync5, realpathSync as realpathSync9, renameSync as renameSync3, rmdirSync as rmdirSync2, writeFileSync as writeFileSync6, unlinkSync as unlinkSync4, chmodSync, openSync as openSync10, closeSync as closeSync10, fsyncSync as fsyncSync4, fstatSync as fstatSync10, readSync as readSync8, constants as constants10, linkSync as linkSync2 } from "fs";
15332
- import { dirname as dirname14, join as join21, resolve as resolve13, relative as relative6, isAbsolute as isAbsolute13, sep as sep6 } from "path";
15507
+ import { dirname as dirname14, join as join23, resolve as resolve13, relative as relative6, isAbsolute as isAbsolute13, sep as sep7 } from "path";
15333
15508
  import { homedir as homedir2 } from "os";
15334
15509
 
15335
15510
  // src/lib/managed-policy.ts
15336
15511
  import { closeSync as closeSync8, constants as constants8, fstatSync as fstatSync8, lstatSync as lstatSync15, openSync as openSync8, readSync as readSync6 } from "fs";
15337
- import { join as join18 } from "path";
15512
+ import { join as join19 } from "path";
15338
15513
  function refusal(reason = "is unreadable") {
15339
15514
  return new SkillSelectionError("INVALID_AGENT_POLICY", `The Skills agent loading policy ${reason}; refusing legacy fallback.`);
15340
15515
  }
@@ -15365,7 +15540,7 @@ function serializeManagedSkillPolicy(value) {
15365
15540
  return text2;
15366
15541
  }
15367
15542
  function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
15368
- const path = join18(dataDir2, "agent-policy.json");
15543
+ const path = join19(dataDir2, "agent-policy.json");
15369
15544
  let descriptor, observed = false;
15370
15545
  try {
15371
15546
  const stat = lstatSync15(path);
@@ -15413,9 +15588,9 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
15413
15588
  }
15414
15589
 
15415
15590
  // src/lib/agent-bridge.ts
15416
- import { createHash as createHash15 } from "crypto";
15591
+ import { createHash as createHash16 } from "crypto";
15417
15592
  import { lstatSync as lstatSync16, readFileSync as readFileSync13, readdirSync as readdirSync7 } from "fs";
15418
- import { join as join19 } from "path";
15593
+ import { join as join20 } from "path";
15419
15594
  var CLI_BRIDGE_NAME = "skills-cli";
15420
15595
  var CLI_BRIDGE_VERSION = 1;
15421
15596
  var markdown = `---
@@ -15432,7 +15607,7 @@ Use the Skills CLI as the skill authority for this station.
15432
15607
 
15433
15608
  This bridge contains no skill payloads. Do not copy skill content into agent-native skill directories or load other native skill copies. If a Skills command refuses authority, selection, integrity, or native drift, report that refusal and use its repair guidance; do not substitute bundled or stale local content. Explicit local draft authoring remains available.
15434
15609
  `;
15435
- var CLI_BRIDGE_DIGEST = createHash15("sha256").update(markdown).digest("hex");
15610
+ var CLI_BRIDGE_DIGEST = createHash16("sha256").update(markdown).digest("hex");
15436
15611
  var CLI_BRIDGE_FILES = Object.freeze({
15437
15612
  "SKILL.md": markdown,
15438
15613
  ".hasna-skills.json": `${JSON.stringify({ managedBy: "@hasna/skills", kind: "cli-bridge", version: CLI_BRIDGE_VERSION, protocol: "selection-v1", contentSha256: CLI_BRIDGE_DIGEST }, null, 2)}
@@ -15447,7 +15622,7 @@ function isOwnedCliBridge(path, expectedPaths) {
15447
15622
  if (JSON.stringify(readdirSync7(path).sort()) !== JSON.stringify(Object.keys(CLI_BRIDGE_FILES).sort()))
15448
15623
  return false;
15449
15624
  return Object.entries(CLI_BRIDGE_FILES).every(([name, content]) => {
15450
- const file = join19(path, name), stat = lstatSync16(file);
15625
+ const file = join20(path, name), stat = lstatSync16(file);
15451
15626
  return stat.isFile() && !stat.isSymbolicLink() && stat.size === Buffer.byteLength(content) && readFileSync13(file, "utf8") === content;
15452
15627
  });
15453
15628
  } catch {
@@ -15725,36 +15900,36 @@ function disableCodexBundledSkills(text2) {
15725
15900
 
15726
15901
  // src/lib/agent-sumi.ts
15727
15902
  import { existsSync as existsSync8, lstatSync as lstatSync17, realpathSync as realpathSync8 } from "fs";
15728
- import { join as join20, resolve as resolve12 } from "path";
15903
+ import { join as join21, resolve as resolve12 } from "path";
15729
15904
  function sumiConfigDirectory(home, env = process.env) {
15730
15905
  for (const key of ["SUMI_CONFIG", "SUMI_CONFIG_CONTENT"])
15731
15906
  if (env[key] !== undefined)
15732
15907
  throw new Error(`Sumi ${key} requires a dedicated discovery adapter; unset it before Skills integration`);
15733
15908
  const selected = (key) => env[key]?.trim() ? env[key] : undefined;
15734
- const expand = (value) => resolve12(value === "~" ? home : value.startsWith("~/") ? join20(home, value.slice(2)) : value);
15909
+ const expand = (value) => resolve12(value === "~" ? home : value.startsWith("~/") ? join21(home, value.slice(2)) : value);
15735
15910
  const explicit = selected("SUMI_CONFIG_DIR");
15736
15911
  const xdg = selected("XDG_CONFIG_HOME");
15737
15912
  const custom = selected("SUMI_HOME");
15738
- const canonical5 = explicit ? expand(explicit) : xdg ? join20(expand(xdg), "sumi") : join20(custom ? expand(custom) : join20(home, ".hasna-internal/sumi"), "config");
15913
+ const canonical6 = explicit ? expand(explicit) : xdg ? join21(expand(xdg), "sumi") : join21(custom ? expand(custom) : join21(home, ".hasna-internal/sumi"), "config");
15739
15914
  if (explicit || xdg || custom)
15740
- return canonical5;
15741
- const legacy = join20(home, ".config/sumi");
15742
- const a = lstatSync17(canonical5, { throwIfNoEntry: false }), b = lstatSync17(legacy, { throwIfNoEntry: false });
15743
- if (a && b && realpathSync8(canonical5) !== realpathSync8(legacy))
15915
+ return canonical6;
15916
+ const legacy = join21(home, ".config/sumi");
15917
+ const a = lstatSync17(canonical6, { throwIfNoEntry: false }), b = lstatSync17(legacy, { throwIfNoEntry: false });
15918
+ if (a && b && realpathSync8(canonical6) !== realpathSync8(legacy))
15744
15919
  throw new Error("Sumi config roots conflict; complete native home adoption before Skills integration");
15745
- const path = a ? canonical5 : b ? legacy : canonical5;
15920
+ const path = a ? canonical6 : b ? legacy : canonical6;
15746
15921
  if (!existsSync8(path))
15747
15922
  return path;
15748
15923
  const physical = realpathSync8(path);
15749
- if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![canonical5, legacy].includes(physical))
15924
+ if ((a?.isSymbolicLink() || b?.isSymbolicLink()) && ![canonical6, legacy].includes(physical))
15750
15925
  throw new Error("Sumi config alias must resolve to its canonical or legacy root");
15751
15926
  return physical;
15752
15927
  }
15753
15928
  function sumiBridgeRoot(home) {
15754
- return join20(sumiConfigDirectory(home), "skills");
15929
+ return join21(sumiConfigDirectory(home), "skills");
15755
15930
  }
15756
15931
  function sumiConfigPath(home) {
15757
- return join20(sumiConfigDirectory(home), "sumi.json");
15932
+ return join21(sumiConfigDirectory(home), "sumi.json");
15758
15933
  }
15759
15934
  function renderSumiPlugin(command, profile) {
15760
15935
  return renderSumiPluginVersion(command, profile, false);
@@ -15847,7 +16022,7 @@ var SUMI_SKILL_PERMISSIONS = [
15847
16022
 
15848
16023
  // src/lib/codex-hook-trust-files.ts
15849
16024
  import { closeSync as closeSync9, constants as constants9, fstatSync as fstatSync9, fsyncSync as fsyncSync3, lstatSync as lstatSync18, openSync as openSync9, readSync as readSync7, writeFileSync as writeFileSync5 } from "fs";
15850
- import { createHash as createHash16 } from "crypto";
16025
+ import { createHash as createHash17 } from "crypto";
15851
16026
  import { dirname as dirname13 } from "path";
15852
16027
  function need5(value, code) {
15853
16028
  if (!value)
@@ -15883,7 +16058,7 @@ function snapshot(file, privateFile = false, options = {}) {
15883
16058
  }
15884
16059
  need5(length === Number(before.size) && sameStat(before, fstatSync9(fd, { bigint: true })) && sameStat(before, lstatSync18(file, { bigint: true })), "INPUT_CHANGED");
15885
16060
  const content = bytes.subarray(0, length), text2 = new TextDecoder("utf-8", { fatal: true }).decode(content);
15886
- return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash16("sha256").update(content).digest("hex") };
16061
+ return { file, privateFile, readOnlyPackage, text: text2, bytes: content, stat: before, sha256: createHash17("sha256").update(content).digest("hex") };
15887
16062
  } finally {
15888
16063
  closeSync9(fd);
15889
16064
  }
@@ -15894,7 +16069,7 @@ function unchanged2(before) {
15894
16069
  }
15895
16070
 
15896
16071
  // src/lib/agent-integration.ts
15897
- var sha3 = (data) => createHash17("sha256").update(data).digest("hex");
16072
+ var sha3 = (data) => createHash18("sha256").update(data).digest("hex");
15898
16073
  var ROOTS = NATIVE_SKILL_ROOTS;
15899
16074
  function hasTrustedDarwinAcl(fd, allowDenyOnly = false) {
15900
16075
  try {
@@ -15972,7 +16147,7 @@ function isSystemRootAlias(path) {
15972
16147
  }
15973
16148
  }
15974
16149
  function canonicalSystemPath(path) {
15975
- const absolute = resolve13(path), root = `/${absolute.split(sep6)[1]}`;
16150
+ const absolute = resolve13(path), root = `/${absolute.split(sep7)[1]}`;
15976
16151
  if (process.platform !== "darwin" || !["/var", "/tmp", "/etc"].includes(root) || !lstatSync19(root, { throwIfNoEntry: false })?.isSymbolicLink())
15977
16152
  return absolute;
15978
16153
  if (!isSystemRootAlias(root))
@@ -15992,7 +16167,7 @@ function assertSafePath(path) {
15992
16167
  }
15993
16168
  function readRootAlias(home, agent) {
15994
16169
  assertSafePath(home);
15995
- const realHome = realpathSync9(home), alias = join21(realHome, `.${agent}`);
16170
+ const realHome = realpathSync9(home), alias = join23(realHome, `.${agent}`);
15996
16171
  const stat = lstatSync19(alias, { throwIfNoEntry: false, bigint: true });
15997
16172
  if (!stat?.isSymbolicLink())
15998
16173
  throw new Error(`Agent root alias changed after planning: ${alias}`);
@@ -16002,7 +16177,7 @@ function readRootAlias(home, agent) {
16002
16177
  if (!targetStat?.isDirectory())
16003
16178
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
16004
16179
  const target = realpathSync9(targetPath), inside = relative6(realHome, target);
16005
- if (!inside || inside === ".." || inside.startsWith(`..${sep6}`) || isAbsolute13(inside))
16180
+ if (!inside || inside === ".." || inside.startsWith(`..${sep7}`) || isAbsolute13(inside))
16006
16181
  throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
16007
16182
  return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
16008
16183
  }
@@ -16011,15 +16186,15 @@ function rootAliases(home, allowed = false) {
16011
16186
  return [];
16012
16187
  const result = [];
16013
16188
  for (const agent of ["claude", "codex"])
16014
- if (lstatSync19(join21(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
16189
+ if (lstatSync19(join23(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
16015
16190
  result.push(readRootAlias(home, agent));
16016
- if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep6) || other.target.startsWith(item.target + sep6))))
16191
+ if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep7) || other.target.startsWith(item.target + sep7))))
16017
16192
  throw new Error("Agent root alias targets must not overlap");
16018
16193
  return result;
16019
16194
  }
16020
16195
  function canonicalAgentPath(path, aliases) {
16021
- const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep6));
16022
- return binding ? join21(binding.target, relative6(binding.alias, absolute)) : absolute;
16196
+ const absolute = canonicalSystemPath(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep7));
16197
+ return binding ? join23(binding.target, relative6(binding.alias, absolute)) : absolute;
16023
16198
  }
16024
16199
  function disabledCodexSkillPaths(config, aliases) {
16025
16200
  const skills = config.skills;
@@ -16042,7 +16217,7 @@ function disabledCodexSkillPaths(config, aliases) {
16042
16217
  } catch {
16043
16218
  continue;
16044
16219
  }
16045
- if (!document2.endsWith(`${sep6}SKILL.md`))
16220
+ if (!document2.endsWith(`${sep7}SKILL.md`))
16046
16221
  continue;
16047
16222
  settings.set(document2, [...settings.get(document2) ?? [], entry.enabled === false]);
16048
16223
  }
@@ -16050,7 +16225,7 @@ function disabledCodexSkillPaths(config, aliases) {
16050
16225
  }
16051
16226
  function recheckRootAliases(aliases) {
16052
16227
  for (const binding of aliases) {
16053
- if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join21(binding.home, `.${binding.agent}`))
16228
+ if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join23(binding.home, `.${binding.agent}`))
16054
16229
  throw new Error("Invalid agent root alias binding");
16055
16230
  const current = readRootAlias(binding.home, binding.agent);
16056
16231
  if (JSON.stringify(current) !== JSON.stringify(binding))
@@ -16088,7 +16263,7 @@ function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
16088
16263
  }
16089
16264
  }
16090
16265
  function treeHash(root) {
16091
- const hash2 = createHash17("sha256");
16266
+ const hash2 = createHash18("sha256");
16092
16267
  let bytes = 0, files = 0;
16093
16268
  function visit2(path) {
16094
16269
  const stat = lstatSync19(path);
@@ -16098,7 +16273,7 @@ function treeHash(root) {
16098
16273
  if (stat.isDirectory()) {
16099
16274
  hash2.update(`d\x00${rel}\x00`);
16100
16275
  for (const name of readdirSync8(path).sort())
16101
- visit2(join21(path, name));
16276
+ visit2(join23(path, name));
16102
16277
  return;
16103
16278
  }
16104
16279
  if (!stat.isFile())
@@ -16126,7 +16301,7 @@ function selectNativeMigrationTargets(inventory, manifest) {
16126
16301
  if (!existsSync9(projectRoot) || !lstatSync19(projectRoot).isDirectory())
16127
16302
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
16128
16303
  assertSafePath(projectRoot);
16129
- if (isAbsolute13(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep6}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
16304
+ if (isAbsolute13(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep7}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
16130
16305
  throw new Error(`Invalid native migration target: ${target.path}`);
16131
16306
  const identity5 = `${target.agent}\x00${canonicalSystemPath(projectRoot)}\x00${normalized}`;
16132
16307
  if (identities.has(identity5))
@@ -16162,7 +16337,7 @@ function projectAncestorDirectories(projects) {
16162
16337
  }
16163
16338
  function isCodexSkillDiscoveryInput(path) {
16164
16339
  for (let cursor = canonicalSystemPath(path);cursor !== dirname14(cursor); cursor = dirname14(cursor)) {
16165
- if (ROOTS.some(([agent, root]) => agent === "codex" && cursor.endsWith(join21(sep6, root))))
16340
+ if (ROOTS.some(([agent, root]) => agent === "codex" && cursor.endsWith(join23(sep7, root))))
16166
16341
  return true;
16167
16342
  }
16168
16343
  return false;
@@ -16179,18 +16354,18 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16179
16354
  const selectedAgents = options.agents ? new Set(options.agents) : undefined;
16180
16355
  const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
16181
16356
  const rootDefinitions = selectedAgents ? ROOTS.filter(([agent, path]) => selectedAgents.has(agent) || selectedAgents.has("hermes") && agent === "codex" && path === ".agents/skills") : ROOTS;
16182
- const roots = rootDefinitions.map(([agent, path]) => [agent, canonicalAgentPath(join21(home, path), aliases)]);
16183
- const bridgePaths = Object.entries(AGENT_ADAPTERS).filter(([agent]) => agent !== "sumi" || includesAgent("sumi")).map(([agent, adapter]) => canonicalAgentPath(join21(agent === "sumi" ? sumiBridgeRoot(home) : join21(home, adapter.root), CLI_BRIDGE_NAME), aliases));
16357
+ const roots = rootDefinitions.map(([agent, path]) => [agent, canonicalAgentPath(join23(home, path), aliases)]);
16358
+ const bridgePaths = Object.entries(AGENT_ADAPTERS).filter(([agent]) => agent !== "sumi" || includesAgent("sumi")).map(([agent, adapter]) => canonicalAgentPath(join23(agent === "sumi" ? sumiBridgeRoot(home) : join23(home, adapter.root), CLI_BRIDGE_NAME), aliases));
16184
16359
  for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
16185
16360
  for (const [agent, path] of rootDefinitions)
16186
- roots.push([agent, canonicalAgentPath(join21(project, path), aliases)]);
16361
+ roots.push([agent, canonicalAgentPath(join23(project, path), aliases)]);
16187
16362
  if (selectedAgents?.has("sumi"))
16188
16363
  for (const directory of [".claude", ".agents"])
16189
16364
  for (const name of ["skill", "skills"])
16190
- roots.push(["sumi", canonicalAgentPath(join21(project, directory, name), aliases)]);
16365
+ roots.push(["sumi", canonicalAgentPath(join23(project, directory, name), aliases)]);
16191
16366
  }
16192
16367
  const installationInputs = options.codexInstallationInputRoots ?? [];
16193
- if (installationInputs.some((input) => resolve13(input) !== input || realpathSync9(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep6) || input.startsWith(path + sep6)))))
16368
+ if (installationInputs.some((input) => resolve13(input) !== input || realpathSync9(input) !== input || roots.some(([agent, path]) => agent === "codex" && (path === input || path.startsWith(input + sep7) || input.startsWith(path + sep7)))))
16194
16369
  throw new Error("Native installation input overlaps an unconditional skill-loading root");
16195
16370
  const entries = [], seen = new Set;
16196
16371
  const cacheScans = new Map, maxAliasProofEntries = 1e4;
@@ -16216,7 +16391,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16216
16391
  if (!scan?.complete || scan.entries > maxAliasProofEntries)
16217
16392
  return false;
16218
16393
  if (scan.hasSkills) {
16219
- const targetSkills = entries.filter((entry) => entry.agent === agent && entry.vendor && (entry.path === target || entry.path.startsWith(target + sep6)));
16394
+ const targetSkills = entries.filter((entry) => entry.agent === agent && entry.vendor && (entry.path === target || entry.path.startsWith(target + sep7)));
16220
16395
  if (path === reviewedCacheAlias && targetSkills.length > 0)
16221
16396
  reviewedCacheAliasFound = true;
16222
16397
  if (agent === "codex" && targetSkills.length > 0 && targetSkills.every((entry) => disabledVendorPaths.has(entry.path) || options.disabledVendorSkill?.(entry) === true))
@@ -16231,7 +16406,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16231
16406
  admit(path);
16232
16407
  const scan = { complete: true, hasSkills: false, entries: 1 };
16233
16408
  assertSafePath(path);
16234
- if (configuredRoot && agent === "codex" && installationInputs.some((root) => path === root || path.startsWith(`${root}${sep6}`)))
16409
+ if (configuredRoot && agent === "codex" && installationInputs.some((root) => path === root || path.startsWith(`${root}${sep7}`)))
16235
16410
  return scan;
16236
16411
  if (!existsSync9(path))
16237
16412
  return scan;
@@ -16243,21 +16418,21 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16243
16418
  }
16244
16419
  if (!stat.isDirectory())
16245
16420
  throw new Error(`Unsupported native discovery entry: ${path}`);
16246
- if (existsSync9(join21(path, "SKILL.md"))) {
16421
+ if (existsSync9(join23(path, "SKILL.md"))) {
16247
16422
  scan.hasSkills = true;
16248
16423
  if (seen.has(path))
16249
16424
  return scan;
16250
16425
  seen.add(path);
16251
16426
  let managed = false;
16252
- const marker = join21(path, ".hasna-skills.json");
16427
+ const marker = join23(path, ".hasna-skills.json");
16253
16428
  if (existsSync9(marker)) {
16254
16429
  try {
16255
16430
  managed = JSON.parse(readFileSync14(marker, "utf8")).managedBy === "@hasna/skills";
16256
16431
  } catch {}
16257
16432
  }
16258
16433
  const bridge = (!vendor || agent === "sumi") && isOwnedCliBridge(path, bridgePaths);
16259
- const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep6));
16260
- entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" || isCodexSkillDiscoveryInput(path) ? { codexHome: process.env.CODEX_HOME ?? join21(home, ".codex") } : {}, ...agent === "codex" && path.startsWith(canonicalAgentPath(join21(home, ".codex", "skills", ".system"), aliases) + sep6) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve13(home) } : {}, ...rootAlias ? { rootAlias } : {} });
16434
+ const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep7));
16435
+ entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" || isCodexSkillDiscoveryInput(path) ? { codexHome: process.env.CODEX_HOME ?? join23(home, ".codex") } : {}, ...agent === "codex" && path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep7) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve13(home) } : {}, ...rootAlias ? { rootAlias } : {} });
16261
16436
  return scan;
16262
16437
  }
16263
16438
  if (depth > (agent === "sumi" ? 32 : vendor ? 32 : 3))
@@ -16266,7 +16441,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16266
16441
  try {
16267
16442
  let child;
16268
16443
  while ((child = directory.readSync()) !== null) {
16269
- admit(join21(path, child.name));
16444
+ admit(join23(path, child.name));
16270
16445
  children.push(child);
16271
16446
  }
16272
16447
  } finally {
@@ -16278,12 +16453,12 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16278
16453
  for (const { name } of children) {
16279
16454
  if (name === "node_modules")
16280
16455
  continue;
16281
- if (name.startsWith(".") && name !== ".system" && agent !== "hermes" && agent !== "sumi" && !(options.guardHermes && `${path}${sep6}`.includes(`${sep6}.agents${sep6}skills${sep6}`)))
16456
+ if (name.startsWith(".") && name !== ".system" && agent !== "hermes" && agent !== "sumi" && !(options.guardHermes && `${path}${sep7}`.includes(`${sep7}.agents${sep7}skills${sep7}`)))
16282
16457
  continue;
16283
16458
  const isVendor = vendor || name === ".system";
16284
16459
  if (isVendor && !options.includeVendor)
16285
16460
  continue;
16286
- const child = join21(path, name);
16461
+ const child = join23(path, name);
16287
16462
  if (isVendor && lstatSync19(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
16288
16463
  const target = statSync9(child, { throwIfNoEntry: false });
16289
16464
  if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child, path)) {
@@ -16301,7 +16476,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16301
16476
  return scan;
16302
16477
  }
16303
16478
  function scanRoot(agent, path, vendor = false, pluginCache = false, configuredRoot = false) {
16304
- if (agent === "hermes" || options.guardHermes && `${path}${sep6}`.includes(`${sep6}.agents${sep6}skills${sep6}`))
16479
+ if (agent === "hermes" || options.guardHermes && `${path}${sep7}`.includes(`${sep7}.agents${sep7}skills${sep7}`))
16305
16480
  assertNoHermesLegacyShadow(path);
16306
16481
  if (!visit2(agent, path, vendor, 0, pluginCache, false, configuredRoot).complete)
16307
16482
  throw new Error(`Native skill discovery limit exceeded; review this root before continuing: ${path}`);
@@ -16310,11 +16485,11 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
16310
16485
  scanRoot(agent, path);
16311
16486
  if (options.includeVendor) {
16312
16487
  for (const agent of ["codex", "claude"].filter(includesAgent))
16313
- scanRoot(agent, canonicalAgentPath(join21(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
16488
+ scanRoot(agent, canonicalAgentPath(join23(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
16314
16489
  if (includesAgent("claude"))
16315
- scanRoot("claude", canonicalAgentPath(join21(home, ".claude", "plugins", "synced"), aliases), true);
16490
+ scanRoot("claude", canonicalAgentPath(join23(home, ".claude", "plugins", "synced"), aliases), true);
16316
16491
  if (includesAgent("gemini"))
16317
- scanRoot("gemini", join21(home, ".gemini", "extensions"), true);
16492
+ scanRoot("gemini", join23(home, ".gemini", "extensions"), true);
16318
16493
  }
16319
16494
  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 }))) : [];
16320
16495
  for (const root of [...options.agentRoots ?? [], ...configured])
@@ -16427,14 +16602,14 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
16427
16602
  function disableCodexSkills(text2, skills, aliases, bridgePath) {
16428
16603
  const bundled = disableCodexBundledSkills(text2);
16429
16604
  let result = normalizeCodexInlinePathConfig(bundled);
16430
- const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve13(bridgePath), resolve13(join21(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
16605
+ const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve13(bridgePath), resolve13(join23(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
16431
16606
  const previousConfig = Bun.TOML.parse(bundled).skills?.config;
16432
16607
  if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
16433
16608
  throw new Error("Codex skill controls require one path or name selector per [[skills.config]] entry; review ambiguous entries before running skills hook install");
16434
16609
  const bridgeNeedsRepair = previousConfig?.some((entry) => entry.enabled === false && bridgeSelector(entry));
16435
16610
  const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
16436
16611
  for (const skill of selections) {
16437
- const path = join21(skill.path, "SKILL.md");
16612
+ const path = join23(skill.path, "SKILL.md");
16438
16613
  let found = false;
16439
16614
  result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
16440
16615
  const parsed = Bun.TOML.parse(section);
@@ -16461,7 +16636,7 @@ enabled = false
16461
16636
  }
16462
16637
  function assertCodexInstallationInputs(home, projectDirs, inputs) {
16463
16638
  const nativeRoots = [home, ...projectAncestorDirectories(projectDirs)].flatMap((directory) => ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => resolve13(directory, path)));
16464
- if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep6) || input.startsWith(root + sep6))))
16639
+ if (inputs.some((input) => nativeRoots.some((root) => root === input || root.startsWith(input + sep7) || input.startsWith(root + sep7))))
16465
16640
  throw new Error("Native installation input overlaps an unconditional skill-loading root");
16466
16641
  }
16467
16642
  function planAgentIntegration(options) {
@@ -16469,7 +16644,7 @@ function planAgentIntegration(options) {
16469
16644
  throw new Error("Explicit Codex skill denials require a fresh native catalog and discovery review");
16470
16645
  const home = options.home ?? homedir2(), dataDir2 = options.dataDir ?? getDataDirReadOnly();
16471
16646
  const aliases = rootAliases(home, options.allowRootAliases);
16472
- const policyPath = join21(dataDir2, "agent-policy.json");
16647
+ const policyPath = join23(dataDir2, "agent-policy.json");
16473
16648
  assertSafePath(policyPath);
16474
16649
  const priorSnapshot = readManagedSkillPolicySnapshot(dataDir2), previousPolicy = priorSnapshot?.text ?? null, policy = priorSnapshot?.value ?? {};
16475
16650
  if (policy.bridge !== undefined && (!policy.bridge || typeof policy.bridge !== "object" || Array.isArray(policy.bridge)))
@@ -16501,28 +16676,28 @@ function planAgentIntegration(options) {
16501
16676
  }
16502
16677
  return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
16503
16678
  });
16504
- const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join21(home, ".codex", "config.toml"), aliases)) : null;
16679
+ const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases)) : null;
16505
16680
  const codexConfig = Bun.TOML.parse(codexConfigBefore ?? "");
16506
16681
  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) });
16507
- const codexPathConfig = options.agents.includes("codex") ? disableCodexSkills(codexConfigBefore ?? "", nativeSkills, aliases, canonicalAgentPath(join21(home, ".codex/skills", CLI_BRIDGE_NAME), aliases)) : undefined;
16682
+ const codexPathConfig = options.agents.includes("codex") ? disableCodexSkills(codexConfigBefore ?? "", nativeSkills, aliases, canonicalAgentPath(join23(home, ".codex/skills", CLI_BRIDGE_NAME), aliases)) : undefined;
16508
16683
  const plannedCodexRules = codexPathConfig === undefined ? undefined : Bun.TOML.parse(codexPathConfig).skills?.config ?? [];
16509
16684
  const observedNativeSources = [];
16510
- const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join21(entry.path, "SKILL.md")), canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), resolve13(options.projectDir ?? home), (path) => {
16685
+ const codexPluginControls = options.codexNativeCatalog ? reviewCodexPluginControls(options.codexNativeCatalog, nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor).map((entry) => join23(entry.path, "SKILL.md")), canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), resolve13(options.projectDir ?? home), (path) => {
16511
16686
  const bytes = readNativeBytes(path, 1024 * 1024);
16512
16687
  observedNativeSources.push({ path, sha256: sha3(bytes) });
16513
16688
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
16514
16689
  }, codexConfig.skills?.config ?? [], codexConfig.plugins, plannedCodexRules, options.codexSkillDenials) : { skills: policy.bridge?.codexPluginSkills ?? [], inactivePlugins: policy.bridge?.codexInactivePlugins ?? [], sourceInputs: policy.bridge?.discovery?.codex?.codexInstallationInputs?.plugins ?? [] };
16515
- const cache = canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases);
16690
+ const cache = canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases);
16516
16691
  const retired = (policy.bridge?.codexPluginSkills ?? []).filter((control) => absentDisabledCodexPluginParent(cache, control.pluginParent, policy.bridge.codexPluginSkills, codexConfig.skills?.config ?? []));
16517
16692
  for (const denial of options.codexSkillDenials ?? []) {
16518
16693
  const control = codexPluginControls.skills.find((item) => item.name === denial.name);
16519
16694
  if (!control)
16520
16695
  throw new Error("Explicit Codex denial did not resolve a reviewed identity");
16521
- const root = join21(control.pluginParent, relative6(control.pluginParent, denial.path).split(sep6)[0]);
16696
+ const root = join23(control.pluginParent, relative6(control.pluginParent, denial.path).split(sep7)[0]);
16522
16697
  const review = options.discoveryInputs.agents.find((item) => item.agent === "codex");
16523
- for (const path of [join21(root, ".app.json"), join21(root, ".mcp.json"), join21(root, "hooks")])
16698
+ for (const path of [join23(root, ".app.json"), join23(root, ".mcp.json"), join23(root, "hooks")])
16524
16699
  observedNativeSources.push({ path, sha256: nativeSourceDigest(path) });
16525
- const files = [join21(root, ".codex-plugin/plugin.json"), ...control.appSha256 ? [join21(root, ".app.json")] : [], ...control.mcpSha256 ? [join21(root, ".mcp.json")] : []];
16700
+ const files = [join23(root, ".codex-plugin/plugin.json"), ...control.appSha256 ? [join23(root, ".app.json")] : [], ...control.mcpSha256 ? [join23(root, ".mcp.json")] : []];
16526
16701
  for (const path of files)
16527
16702
  if (!review?.sources.some((source) => source.path === path && source.format === undefined && source.fields === undefined && [undefined, "bytes"].includes(source.hashMode) && source.sha256 === sha3(readNativeBytes(path, 1024 * 1024))))
16528
16703
  throw new Error("Explicit Codex denial requires current full manifest and capability discovery witnesses");
@@ -16531,12 +16706,12 @@ function planAgentIntegration(options) {
16531
16706
  for (const discovery of discoveries)
16532
16707
  if (discovery.agent === "codex" && discovery.method === "reviewed")
16533
16708
  discovery.codexDisabledPluginSkills = codexPluginSkills;
16534
- if (options.agents.includes("codex") && !options.codexNativeCatalog && codexPluginSkills.length && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), codexPluginSkills, (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), codexConfig.skills?.config ?? [])) {
16709
+ if (options.agents.includes("codex") && !options.codexNativeCatalog && codexPluginSkills.length && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), codexPluginSkills, (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), codexConfig.skills?.config ?? [])) {
16535
16710
  throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; provide a fresh native catalog and discovery review");
16536
16711
  }
16537
16712
  const codexInactivePlugins = codexPluginControls.inactivePlugins;
16538
16713
  const codexDiscovery = discoveries.find((binding) => binding.agent === "codex");
16539
- const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot + sep6) || input.sourceRoot.startsWith(root + sep6)) && codexDiscovery.sources.some((source) => source.path === join21(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
16714
+ const codexPluginSourceInputs = codexPluginControls.sourceInputs.filter((input) => codexDiscovery?.method === "reviewed" && codexDiscovery.roots.some((root) => root === input.sourceRoot || root.startsWith(input.sourceRoot + sep7) || input.sourceRoot.startsWith(root + sep7)) && codexDiscovery.sources.some((source) => source.path === join23(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined));
16540
16715
  assertCodexInstallationInputs(home, [options.projectDir ?? home], codexPluginSourceInputs.map((input) => input.sourceRoot));
16541
16716
  const changes = [];
16542
16717
  let observedSettings;
@@ -16545,19 +16720,19 @@ function planAgentIntegration(options) {
16545
16720
  throw new Error(`Unsupported agent: ${agent}`);
16546
16721
  const { command, profileId: profileId2 } = bindings.get(agent);
16547
16722
  const adapter = AGENT_ADAPTERS[agent];
16548
- const bridgePath = canonicalAgentPath(join21(agent === "sumi" ? sumiBridgeRoot(home) : join21(home, adapter.root), CLI_BRIDGE_NAME), aliases);
16723
+ const bridgePath = canonicalAgentPath(join23(agent === "sumi" ? sumiBridgeRoot(home) : join23(home, adapter.root), CLI_BRIDGE_NAME), aliases);
16549
16724
  assertSafePath(bridgePath);
16550
16725
  if (existsSync9(bridgePath) && !isOwnedCliBridge(bridgePath, [bridgePath]))
16551
16726
  throw new Error(`Refusing to overwrite an unrecognized or modified Skills bridge: ${bridgePath}`);
16552
16727
  for (const [name, after2] of Object.entries(CLI_BRIDGE_FILES)) {
16553
- const path2 = join21(bridgePath, name), before2 = readOptional(path2);
16728
+ const path2 = join23(bridgePath, name), before2 = readOptional(path2);
16554
16729
  if (before2 !== after2)
16555
16730
  changes.push({ path: path2, before: before2, after: after2 });
16556
16731
  }
16557
- const path = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) : join21(home, adapter.config), aliases);
16732
+ const path = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) : join23(home, adapter.config), aliases);
16558
16733
  const before = readOptional(path);
16559
16734
  if (agent === "hermes") {
16560
- const supervisorPath = join21(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(command, profileId2);
16735
+ const supervisorPath = join23(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(command, profileId2);
16561
16736
  const priorCommand = policy.bridge?.commands?.hermes, priorProfile = policy.bridge?.profiles?.hermes ?? policy.profileId;
16562
16737
  const previous = policy.bridge?.supervisors?.hermes;
16563
16738
  if (supervisorBefore !== null && supervisorBefore !== supervisorAfter && !(previous?.path === supervisorPath && typeof priorCommand === "string" && typeof priorProfile === "string" && supervisorBefore === renderHermesSupervisor(priorCommand, priorProfile)))
@@ -16568,7 +16743,7 @@ function planAgentIntegration(options) {
16568
16743
  const after2 = configureHermesHooks(before, supervisor, previous);
16569
16744
  if (before !== after2)
16570
16745
  changes.push({ path, before, after: after2 });
16571
- const optOut = join21(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
16746
+ const optOut = join23(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
16572
16747
  if (optOutBefore === null)
16573
16748
  changes.push({ path: optOut, before: null, after: `Managed by @hasna/skills: bundled native skill reseeding is disabled.
16574
16749
  ` });
@@ -16579,7 +16754,7 @@ function planAgentIntegration(options) {
16579
16754
  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))))
16580
16755
  throw new Error("Expected Sumi native permission rules");
16581
16756
  config.permissions = [...(config.permissions ?? []).filter((rule) => rule.action !== "skill"), ...SUMI_SKILL_PERMISSIONS];
16582
- const pluginPath = join21(sumiConfigDirectory(home), "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
16757
+ const pluginPath = join23(sumiConfigDirectory(home), "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
16583
16758
  const pluginAfter = renderSumiPlugin(command, profileId2);
16584
16759
  const priorCommand = policy.bridge?.commands?.sumi, priorProfile = policy.bridge?.profiles?.sumi ?? policy.profileId;
16585
16760
  if (pluginBefore !== null && pluginBefore !== pluginAfter && !(typeof priorCommand === "string" && typeof priorProfile === "string" && isManagedSumiPlugin(pluginBefore, priorCommand, priorProfile)))
@@ -16591,7 +16766,7 @@ function planAgentIntegration(options) {
16591
16766
  if (!permission || typeof permission !== "object" || Array.isArray(permission))
16592
16767
  throw new Error("Expected OpenCode permission object");
16593
16768
  config.permission = { ...permission, skill: { "*": "deny", [CLI_BRIDGE_NAME]: "allow" } };
16594
- const pluginPath = join21(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
16769
+ const pluginPath = join23(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
16595
16770
  const pluginAfter = renderOpenCodePlugin(command, profileId2);
16596
16771
  const priorCommand = policy.bridge?.commands?.opencode;
16597
16772
  const ownedPrevious = typeof priorCommand === "string" && typeof policy.profileId === "string" && pluginBefore === renderOpenCodePlugin(priorCommand, policy.bridge?.profiles?.opencode ?? policy.profileId);
@@ -16608,7 +16783,7 @@ function planAgentIntegration(options) {
16608
16783
  if (before !== after)
16609
16784
  changes.push({ path, before, after });
16610
16785
  if (agent === "codex") {
16611
- const configPath = canonicalAgentPath(join21(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
16786
+ const configPath = canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
16612
16787
  if (previous !== codexConfigBefore)
16613
16788
  throw new Error(`Configuration changed during planning: ${configPath}`);
16614
16789
  const next = disableReviewedCodexPluginNames(codexPathConfig, codexPluginSkills);
@@ -16620,16 +16795,16 @@ function planAgentIntegration(options) {
16620
16795
  }
16621
16796
  }
16622
16797
  }
16623
- const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(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(join21(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join21(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview;
16798
+ const codexPluginSkillReview = options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(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(join23(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join23(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview;
16624
16799
  const discoveryAfter = discoveries.map((binding) => {
16625
16800
  const rebound = rebindAgentDiscovery(binding, new Map(changes.map((change) => [change.path, change.after])));
16626
16801
  if (binding.agent !== "codex")
16627
16802
  return rebound;
16628
16803
  const { codexInstallationInputs: previousInputs, ...current } = rebound;
16629
16804
  const inputs = codexPluginSourceInputs.map((input) => input.sourceRoot);
16630
- const directories = options.codexNativeCatalog || !previousInputs ? captureDiscoveryDirectories((binding.directories ?? []).filter((directory) => inputs.some((root) => root.startsWith(directory.path + sep6))).map((directory) => directory.path), inputs) : previousInputs.directories;
16805
+ const directories = options.codexNativeCatalog || !previousInputs ? captureDiscoveryDirectories((binding.directories ?? []).filter((directory) => inputs.some((root) => root.startsWith(directory.path + sep7))).map((directory) => directory.path), inputs) : previousInputs.directories;
16631
16806
  const installed = codexPluginSourceInputs.length ? { ...current, codexInstallationInputs: { version: "codex-cli 0.160.0", catalogSha256: codexPluginSkillReview.catalogSha256, plugins: codexPluginSourceInputs, ...directories?.length ? { directories } : {} } } : current;
16632
- return captureRetiredCodexDiscovery(installed, codexPluginSkills, Bun.TOML.parse(changes.find((change) => change.path === canonicalAgentPath(join21(home, ".codex/config.toml"), aliases))?.after ?? codexConfigBefore ?? "").skills?.config ?? []);
16807
+ return captureRetiredCodexDiscovery(installed, codexPluginSkills, Bun.TOML.parse(changes.find((change) => change.path === canonicalAgentPath(join23(home, ".codex/config.toml"), aliases))?.after ?? codexConfigBefore ?? "").skills?.config ?? []);
16633
16808
  });
16634
16809
  const nextPolicy = { ...policy, version: 1, loading: "cli", profileId, bridge: {
16635
16810
  ...policy.bridge,
@@ -16638,10 +16813,10 @@ function planAgentIntegration(options) {
16638
16813
  agents: [...new Set([...priorAgents, ...options.agents])].sort(),
16639
16814
  home: resolve13(home),
16640
16815
  includeVendor: true,
16641
- ...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join21(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha3(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
16816
+ ...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join23(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha3(renderHermesSupervisor(bindings.get("hermes").command, bindings.get("hermes").profileId)) } } } : {},
16642
16817
  commands: { ...policy.bridge?.commands, ...Object.fromEntries([...bindings].map(([agent, binding]) => [agent, binding.command])) },
16643
16818
  profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
16644
- disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join21(home, ".codex", "skills", ".system"), aliases) + sep6)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
16819
+ disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep7)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
16645
16820
  rootAliases: aliases,
16646
16821
  ...options.codexNativeCatalog || codexPluginSkills.length || codexInactivePlugins.length || codexPluginSourceInputs.length ? { codexPluginSkills, codexInactivePlugins, codexPluginSkillReview } : {},
16647
16822
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
@@ -16693,10 +16868,10 @@ function projectedHookExecutable(command, expectedResolved) {
16693
16868
  if (expectedResolved !== undefined && resolved !== expectedResolved)
16694
16869
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
16695
16870
  const resolvedParentIdentity = parents2(resolved);
16696
- const manifest = snapshot(join21(dirname14(dirname14(resolved)), "package.json"), false, { readOnlyPackage: true });
16871
+ const manifest = snapshot(join23(dirname14(dirname14(resolved)), "package.json"), false, { readOnlyPackage: true });
16697
16872
  projectedHookAcl(manifest.file, manifest.stat);
16698
16873
  const pkg = JSON.parse(manifest.text);
16699
- if (pkg.name !== "@hasna/skills" || !/^\d+\.\d+\.\d+$/.test(pkg.version) || pkg.bin?.skills !== "bin/index.js" || resolved !== join21(dirname14(manifest.file), "bin/index.js"))
16874
+ if (pkg.name !== "@hasna/skills" || !/^\d+\.\d+\.\d+$/.test(pkg.version) || pkg.bin?.skills !== "bin/index.js" || resolved !== join23(dirname14(manifest.file), "bin/index.js"))
16700
16875
  hookProjectionRefusal("EXECUTABLE_UNVERIFIED");
16701
16876
  const cli = snapshot(resolved, false, { readOnlyPackage: true });
16702
16877
  projectedHookAcl(cli.file, cli.stat);
@@ -16733,7 +16908,7 @@ function planClaudeManagedHookProjection(options) {
16733
16908
  hookProjectionRefusal("TARGET_INVALID");
16734
16909
  }
16735
16910
  const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
16736
- const policyPath = join21(dataDir2, "agent-policy.json");
16911
+ const policyPath = join23(dataDir2, "agent-policy.json");
16737
16912
  let policyBytes, settingsBytes, settingsPath, command, profile;
16738
16913
  try {
16739
16914
  policyBytes = readNativeBytes(policyPath, 1024 * 1024);
@@ -16741,7 +16916,7 @@ function planClaudeManagedHookProjection(options) {
16741
16916
  if (!snapshot2 || snapshot2.text !== policyBytes.toString("utf8"))
16742
16917
  hookProjectionRefusal("SOURCE_UNVERIFIED");
16743
16918
  const binding = snapshot2.value.bridge;
16744
- settingsPath = canonicalAgentPath(join21(home, AGENT_ADAPTERS.claude.config), binding?.rootAliases ?? []);
16919
+ settingsPath = canonicalAgentPath(join23(home, AGENT_ADAPTERS.claude.config), binding?.rootAliases ?? []);
16745
16920
  settingsBytes = readNativeBytes(settingsPath, 1024 * 1024);
16746
16921
  command = binding?.commands?.claude;
16747
16922
  profile = binding?.profiles?.claude;
@@ -16824,12 +16999,12 @@ function planClaudePreToolUseHookUpdate(options) {
16824
16999
  return planClaudeHookEventsUpdate({ ...options, events: ["PreToolUse"] });
16825
17000
  }
16826
17001
  function claudeSettingsConsumers(home, path, discoveries, aliases) {
16827
- const canonical5 = (value) => canonicalAgentPath(value, aliases);
17002
+ const canonical6 = (value) => canonicalAgentPath(value, aliases);
16828
17003
  const containsSettings = (root) => {
16829
- const distance = relative6(canonical5(root), path);
16830
- return distance === "" || distance !== ".." && !distance.startsWith(`..${sep6}`) && !isAbsolute13(distance);
17004
+ const distance = relative6(canonical6(root), path);
17005
+ return distance === "" || distance !== ".." && !distance.startsWith(`..${sep7}`) && !isAbsolute13(distance);
16831
17006
  };
16832
- return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical5(join21(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical5(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
17007
+ return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical6(join23(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical6(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
16833
17008
  }
16834
17009
  function verifyCoordinatedDiscovery(binding, home, aliases) {
16835
17010
  try {
@@ -16847,7 +17022,7 @@ function verifyCoordinatedDiscovery(binding, home, aliases) {
16847
17022
  }
16848
17023
  function planClaudeHookEventsUpdate(options) {
16849
17024
  const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
16850
- const policyPath = join21(dataDir2, "agent-policy.json");
17025
+ const policyPath = join23(dataDir2, "agent-policy.json");
16851
17026
  assertSafePath(policyPath);
16852
17027
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
16853
17028
  if (!snapshot2)
@@ -16858,7 +17033,7 @@ function planClaudeHookEventsUpdate(options) {
16858
17033
  }
16859
17034
  const aliases = bridge.rootAliases ?? [];
16860
17035
  recheckRootAliases(aliases);
16861
- const path = canonicalAgentPath(join21(home, ".claude", "settings.json"), aliases);
17036
+ const path = canonicalAgentPath(join23(home, ".claude", "settings.json"), aliases);
16862
17037
  assertSafePath(path);
16863
17038
  const before = lstatSync19(path, { throwIfNoEntry: false }) ? new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(readNativeBytes(path, 1024 * 1024)) : null;
16864
17039
  if ((before === null ? "absent" : sha3(before)) !== options.expectedSettingsSha256)
@@ -16936,7 +17111,7 @@ function planAgentSettingsWitnessUpgrade(options) {
16936
17111
  const binding = policy.bridge?.discovery?.[options.agent];
16937
17112
  if (!binding || binding.agent !== options.agent || binding.method !== "reviewed")
16938
17113
  throw new Error("Settings witness upgrade requires an existing reviewed native binding");
16939
- const configPath = canonicalAgentPath(options.agent === "sumi" ? sumiConfigPath(home) : join21(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
17114
+ const configPath = canonicalAgentPath(options.agent === "sumi" ? sumiConfigPath(home) : join23(home, options.agent === "claude" ? ".claude/settings.json" : ".codex/config.toml"), aliases);
16940
17115
  const settings = readOptional(configPath);
16941
17116
  if (settings === null || sha3(settings) !== options.expectedSettingsSha256)
16942
17117
  throw new Error("Native settings preimage changed");
@@ -16987,7 +17162,7 @@ function planAgentSettingsWitnessUpgrade(options) {
16987
17162
  const replacement = { ...binding, sources: rebound };
16988
17163
  verifyAgentDiscovery(replacement);
16989
17164
  const after = serializeManagedSkillPolicy({ ...policy, bridge: { ...policy.bridge, discovery: { ...policy.bridge.discovery, [options.agent]: replacement } } });
16990
- return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join21(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join21(dataDir2, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
17165
+ return { dataDir: dataDir2, profileId: policy.profileId, changes: [{ path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text, after }], nativeSkills: [], observedPolicy: { path: join23(dataDir2, "agent-policy.json"), before: snapshot2.text }, observedSettings: { path: configPath, before: settings }, discoveryBefore: [replacement], discoveryAfter: [replacement], rootAliases: aliases, managedAgentChecks: { home, agents: [options.agent] }, settingsWitnessUpgrade: { agent: options.agent, path: configPath, fromHashMode: previous.hashMode ?? "bytes", fromSha256: previous.sha256, toHashMode: next.hashMode, toSha256: next.sha256, reviewedPreimage: options.reviewedPreimage, currentSettingsSha256: options.expectedSettingsSha256, replacedWitnesses: replaced.map((source) => ({ hashMode: source.hashMode ?? "bytes", sha256: source.sha256 })) } };
16991
17166
  }
16992
17167
  function applyAgentIntegration(plan, options = {}) {
16993
17168
  const roots = plan.changes.flatMap((change) => {
@@ -17003,7 +17178,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
17003
17178
  throw new Error("Native identity source changed after planning");
17004
17179
  if (plan.observedSettings && readOptional(plan.observedSettings.path) !== plan.observedSettings.before)
17005
17180
  throw new Error("Native settings changed after witness planning");
17006
- const policyPath = join21(plan.dataDir, "agent-policy.json");
17181
+ const policyPath = join23(plan.dataDir, "agent-policy.json");
17007
17182
  assertSafePath(policyPath);
17008
17183
  const currentText = (path) => resolve13(path) === resolve13(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
17009
17184
  readManagedSkillPolicySnapshot(plan.dataDir);
@@ -17038,7 +17213,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
17038
17213
  for (const change of plan.changes)
17039
17214
  if (currentText(change.path) !== change.before)
17040
17215
  throw new Error(`Configuration changed after planning: ${change.path}`);
17041
- const backupRoot = join21(plan.dataDir, "migration", randomUUID3()), backups = [], written = [], createdDirectories = new Set;
17216
+ const backupRoot = join23(plan.dataDir, "migration", randomUUID3()), backups = [], written = [], createdDirectories = new Set;
17042
17217
  if (!plan.changes.length) {
17043
17218
  assertCurrent();
17044
17219
  recheckRootAliases(aliases);
@@ -17049,7 +17224,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
17049
17224
  chmodSync(backupRoot, 448);
17050
17225
  for (const [index, change] of plan.changes.entries())
17051
17226
  if (change.before !== null) {
17052
- const backup = join21(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
17227
+ const backup = join23(backupRoot, `${index}-${sha3(change.path).slice(0, 12)}.backup`);
17053
17228
  writeFileSync6(backup, change.before, { mode: 384, flag: "wx" });
17054
17229
  if (readFileSync14(backup, "utf8") !== change.before)
17055
17230
  throw new Error("Native configuration preservation readback failed");
@@ -17082,7 +17257,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
17082
17257
  }
17083
17258
  for (const agent of plan.managedAgentChecks?.agents ?? [])
17084
17259
  assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.home });
17085
- atomicWrite2(join21(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha3(change.before), afterHash: sha3(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
17260
+ atomicWrite2(join23(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha3(change.before), afterHash: sha3(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
17086
17261
  `);
17087
17262
  } catch (error) {
17088
17263
  assertCurrent();
@@ -17145,7 +17320,7 @@ function archiveNativeSkills(inventory, options) {
17145
17320
  const selected = options.targetManifest ? selectNativeMigrationTargets(inventory, options.targetManifest) : inventory.filter((entry) => !entry.bridge && !entry.system && (entry.vendor ? options.includeVendor : entry.managed || options.includeUnmanaged));
17146
17321
  const roots = selected.flatMap((entry) => {
17147
17322
  const root = (entry.rootAlias?.agent === "codex" ? entry.rootAlias.alias : undefined) ?? codexCorpusRootForPath(entry.path);
17148
- const owner = entry.codexHome ?? (entry.agent === "codex" && entry.bridgeHome ? join21(entry.bridgeHome, ".codex") : undefined);
17323
+ const owner = entry.codexHome ?? (entry.agent === "codex" && entry.bridgeHome ? join23(entry.bridgeHome, ".codex") : undefined);
17149
17324
  if (!root && !owner && (entry.agent === "codex" || isCodexSkillDiscoveryInput(entry.path)))
17150
17325
  throw new Error("CODEX_CORPUS_ROOT_UNVERIFIED");
17151
17326
  return [...new Set([root, owner].filter((value) => value !== undefined))];
@@ -17160,7 +17335,7 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
17160
17335
  recheckRootAliases(aliases);
17161
17336
  for (const entry of inventory.filter((entry2) => entry2.bridge)) {
17162
17337
  const adapter = AGENT_ADAPTERS[entry.agent];
17163
- const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join21(entry.agent === "sumi" ? sumiBridgeRoot(entry.bridgeHome) : join21(entry.bridgeHome, adapter.root), CLI_BRIDGE_NAME), aliases) : undefined;
17338
+ const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join23(entry.agent === "sumi" ? sumiBridgeRoot(entry.bridgeHome) : join23(entry.bridgeHome, adapter.root), CLI_BRIDGE_NAME), aliases) : undefined;
17164
17339
  if (!expected || treeHash(entry.path) !== entry.hash || !isOwnedCliBridge(entry.path, [expected]))
17165
17340
  throw new Error(`Skills bridge changed after planning: ${entry.path}`);
17166
17341
  }
@@ -17171,11 +17346,11 @@ function archiveNativeSkillsUnlocked(inventory, options, assertCurrent) {
17171
17346
  const targetManifest = options.targetManifest ? { schema: options.targetManifest.schema, digest: options.targetManifest.digest, targetCount: options.targetManifest.targets.length } : undefined;
17172
17347
  if (!selected.length)
17173
17348
  return { entries: [], ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
17174
- const operationId = randomUUID3(), archiveRoot = join21(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join21(archiveRoot, "receipt.json");
17349
+ const operationId = randomUUID3(), archiveRoot = join23(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join23(archiveRoot, "receipt.json");
17175
17350
  const entries = selected.map((entry, index) => ({
17176
- source: entry.vendor ? join21(entry.path, "SKILL.md") : entry.path,
17177
- archive: join21(archiveRoot, `${index}-${sha3(entry.path).slice(0, 12)}`),
17178
- hash: entry.vendor ? sha3(readNativeBytes(join21(entry.path, "SKILL.md"))) : entry.hash,
17351
+ source: entry.vendor ? join23(entry.path, "SKILL.md") : entry.path,
17352
+ archive: join23(archiveRoot, `${index}-${sha3(entry.path).slice(0, 12)}`),
17353
+ hash: entry.vendor ? sha3(readNativeBytes(join23(entry.path, "SKILL.md"))) : entry.hash,
17179
17354
  ...entry.vendor ? { discoveryOnly: true } : {},
17180
17355
  status: "planned"
17181
17356
  }));
@@ -17282,9 +17457,9 @@ function isInertCodexPluginCacheCopy(entry, cache, allowedHostPaths, read2) {
17282
17457
  try {
17283
17458
  if (entry.agent !== "codex" || !entry.vendor || entry.bridge || entry.system)
17284
17459
  return false;
17285
- const document2 = join21(entry.path, "SKILL.md");
17460
+ const document2 = join23(entry.path, "SKILL.md");
17286
17461
  const inside = relative6(cache, entry.path);
17287
- if (!inside || isAbsolute13(inside) || inside.split(sep6).some((segment) => segment === ".."))
17462
+ if (!inside || isAbsolute13(inside) || inside.split(sep7).some((segment) => segment === ".."))
17288
17463
  return false;
17289
17464
  assertSafePath(entry.path);
17290
17465
  if (!lstatSync19(cache).isDirectory() || !lstatSync19(entry.path).isDirectory() || !lstatSync19(document2).isFile())
@@ -17305,7 +17480,7 @@ function assertManagedAgentBridge(agent, options = {}) {
17305
17480
  }
17306
17481
  function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
17307
17482
  const home = resolve13(options.home ?? homedir2()), dataDir2 = options.dataDir ?? getDataDirReadOnly();
17308
- assertSafePath(join21(dataDir2, "agent-policy.json"));
17483
+ assertSafePath(join23(dataDir2, "agent-policy.json"));
17309
17484
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir2);
17310
17485
  if (!snapshot2)
17311
17486
  throw new Error("NATIVE_SKILL_DRIFT: install the Skills bridge with skills hook install");
@@ -17316,14 +17491,14 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
17316
17491
  if (!Array.isArray(aliases))
17317
17492
  throw new Error("NATIVE_SKILL_DRIFT: invalid root alias binding");
17318
17493
  recheckRootAliases(aliases);
17319
- const expected = canonicalAgentPath(join21(agent === "sumi" ? sumiBridgeRoot(home) : join21(home, AGENT_ADAPTERS[agent].root), CLI_BRIDGE_NAME), aliases);
17494
+ const expected = canonicalAgentPath(join23(agent === "sumi" ? sumiBridgeRoot(home) : join23(home, AGENT_ADAPTERS[agent].root), CLI_BRIDGE_NAME), aliases);
17320
17495
  if (!isOwnedCliBridge(expected, [expected]))
17321
17496
  throw new Error("NATIVE_SKILL_DRIFT: the native Skills bridge is missing or modified; repair it before continuing");
17322
17497
  const roots = projectAncestorDirectories([options.projectDir ?? process.cwd(), ...options.projectDirs ?? [], ...agent === "hermes" && process.env.TERMINAL_CWD ? [process.cwd()] : []]);
17323
- const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes", "sumi"].includes(agent) && entry.path.includes(`${sep6}.agents${sep6}skills${sep6}`) || ["opencode", "sumi"].includes(agent) && entry.agent === "claude";
17498
+ const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes", "sumi"].includes(agent) && entry.path.includes(`${sep7}.agents${sep7}skills${sep7}`) || ["opencode", "sumi"].includes(agent) && entry.agent === "claude";
17324
17499
  const discovery = provenDiscovery ?? binding.discovery?.[agent];
17325
17500
  assertProjectDiscovery(agent, [...roots], home, (path) => canonicalAgentPath(path, aliases), discovery);
17326
- const configPath = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) : join21(home, AGENT_ADAPTERS[agent].config), aliases), config = agent === "hermes" ? parseHermesConfig(readOptional(configPath)) : jsonObject(readOptional(configPath), configPath);
17501
+ const configPath = canonicalAgentPath(agent === "sumi" ? sumiConfigPath(home) : join23(home, AGENT_ADAPTERS[agent].config), aliases), config = agent === "hermes" ? parseHermesConfig(readOptional(configPath)) : jsonObject(readOptional(configPath), configPath);
17327
17502
  const command = binding.commands?.[agent], profile = binding.profiles?.[agent];
17328
17503
  if (typeof command !== "string" || typeof profile !== "string")
17329
17504
  throw new Error("NATIVE_SKILL_DRIFT: the native hook command/profile binding is missing");
@@ -17331,14 +17506,14 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
17331
17506
  throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
17332
17507
  if (agent === "hermes") {
17333
17508
  const supervisor = binding.supervisors?.hermes;
17334
- if (supervisor?.path !== join21(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha3(renderHermesSupervisor(command, profile)))
17509
+ if (supervisor?.path !== join23(dataDir2, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha3(renderHermesSupervisor(command, profile)))
17335
17510
  throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
17336
17511
  assertHermesProtection(home, config, command, profile, supervisor);
17337
17512
  } else if (agent === "sumi") {
17338
- if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || !isManagedSumiPlugin(readOptional(join21(sumiConfigDirectory(home), "plugins", "skills-cli.js")), command, profile))
17513
+ if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || !isManagedSumiPlugin(readOptional(join23(sumiConfigDirectory(home), "plugins", "skills-cli.js")), command, profile))
17339
17514
  throw new Error("NATIVE_SKILL_DRIFT: Sumi bridge protection changed; run skills hook install");
17340
17515
  } else if (agent === "opencode") {
17341
- if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(join21(home, ".config", "opencode", "plugins", "skills-cli.js")) !== renderOpenCodePlugin(command, profile))
17516
+ if (JSON.stringify(config.permission?.skill) !== JSON.stringify({ "*": "deny", [CLI_BRIDGE_NAME]: "allow" }) || readOptional(join23(home, ".config", "opencode", "plugins", "skills-cli.js")) !== renderOpenCodePlugin(command, profile))
17342
17517
  throw new Error("NATIVE_SKILL_DRIFT: OpenCode bridge protection changed; run skills hook install");
17343
17518
  } else {
17344
17519
  const required = {};
@@ -17355,42 +17530,42 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
17355
17530
  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)))
17356
17531
  throw new Error("NATIVE_SKILL_DRIFT: Gemini bridge or bundled-skill protection changed; run skills hook install");
17357
17532
  }
17358
- const codexPath = canonicalAgentPath(join21(home, ".codex", "config.toml"), aliases);
17533
+ const codexPath = canonicalAgentPath(join23(home, ".codex", "config.toml"), aliases);
17359
17534
  if (options.codexDiscoveryRecovery && (agent !== "codex" || options.codexDiscoveryRecovery.configPath !== codexPath))
17360
17535
  throw new Error("NATIVE_SKILL_DRIFT: Codex trust recovery names a different configuration root");
17361
17536
  const codexConfig = agent === "codex" ? Bun.TOML.parse(readOptional(codexPath) ?? "") : {};
17362
17537
  if (agent === "codex" && codexConfig.skills?.bundled?.enabled !== false)
17363
17538
  throw new Error("NATIVE_SKILL_DRIFT: Codex bundled skill reseeding is not disabled (skills.bundled.enabled); run skills hook install");
17364
- const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join21(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
17539
+ const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join23(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
17365
17540
  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))
17366
17541
  throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
17367
- if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), codexConfig.skills?.config ?? []))
17542
+ if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), codexConfig.skills?.config ?? []))
17368
17543
  throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
17369
17544
  const disabledPlugin = (entry) => {
17370
17545
  if (agent !== "codex" || !entry.vendor)
17371
17546
  return false;
17372
- const document2 = join21(entry.path, "SKILL.md"), cache = canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024));
17547
+ const document2 = join23(entry.path, "SKILL.md"), cache = canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024));
17373
17548
  return isReviewedCodexPluginInactive(document2, cache, binding.codexInactivePlugins ?? [], codexConfig.plugins, codexConfig.skills?.config ?? [], read2) || isReviewedCodexPluginSkillDisabled(document2, cache, binding.codexPluginSkills ?? [], codexConfig.skills?.config, read2);
17374
17549
  };
17375
17550
  const disabledPaths = new Set(disabledCodexSkillPaths(codexConfig, aliases));
17376
17551
  const disabledVendorSkill = (entry) => {
17377
17552
  if (agent !== "codex" || !entry.vendor)
17378
17553
  return false;
17379
- if ((binding.codexPluginSkills ?? []).some((control) => entry.path.startsWith(control.pluginParent + sep6)))
17554
+ if ((binding.codexPluginSkills ?? []).some((control) => entry.path.startsWith(control.pluginParent + sep7)))
17380
17555
  return disabledPlugin(entry);
17381
17556
  if (disabledPlugin(entry))
17382
17557
  return true;
17383
17558
  if (!disabledPaths.has(entry.path))
17384
17559
  return false;
17385
- if (entry.path.startsWith(canonicalAgentPath(join21(home, ".codex", "plugins", "cache"), aliases) + sep6))
17560
+ if (entry.path.startsWith(canonicalAgentPath(join23(home, ".codex", "plugins", "cache"), aliases) + sep7))
17386
17561
  return true;
17387
- const runtimeCache = canonicalAgentPath(join21(home, ".cache", "codex-runtimes"), aliases);
17388
- if (entry.path.startsWith(runtimeCache + sep6) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep6)))
17562
+ const runtimeCache = canonicalAgentPath(join23(home, ".cache", "codex-runtimes"), aliases);
17563
+ if (entry.path.startsWith(runtimeCache + sep7) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep7)))
17389
17564
  return true;
17390
- const bundledMarketplaces = canonicalAgentPath(join21(home, ".codex", ".tmp", "bundled-marketplaces"), aliases);
17391
- if (entry.path.startsWith(bundledMarketplaces + sep6) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep6)))
17565
+ const bundledMarketplaces = canonicalAgentPath(join23(home, ".codex", ".tmp", "bundled-marketplaces"), aliases);
17566
+ if (entry.path.startsWith(bundledMarketplaces + sep7) && discovery?.method === "automatic" && discovery.roots.some((root) => entry.path.startsWith(root + sep7)))
17392
17567
  return true;
17393
- if (!entry.path.startsWith(canonicalAgentPath(join21(home, ".codex", "skills", ".system"), aliases) + sep6))
17568
+ if (!entry.path.startsWith(canonicalAgentPath(join23(home, ".codex", "skills", ".system"), aliases) + sep7))
17394
17569
  return false;
17395
17570
  return Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
17396
17571
  };
@@ -17413,7 +17588,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
17413
17588
  let installationInputRoots = [];
17414
17589
  if (agent === "codex") {
17415
17590
  try {
17416
- installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(join21(home, ".codex/plugins/cache"), aliases), discovery.codexInstallationInputs?.plugins ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
17591
+ installationInputRoots = reviewedCodexPluginSourceRoots(canonicalAgentPath(join23(home, ".codex/plugins/cache"), aliases), discovery.codexInstallationInputs?.plugins ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)));
17417
17592
  } catch {
17418
17593
  throw new Error("NATIVE_SKILL_DRIFT: native installation input identity changed; review discovery and catalog");
17419
17594
  }
@@ -17423,7 +17598,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
17423
17598
  let adapterRefusal, inert = [];
17424
17599
  if (agent === "codex" && unexpected.length && options.codexNativePolicy) {
17425
17600
  const { inspector, helperRunner, deadlineMs, ...envelope } = options.codexNativePolicy;
17426
- const cache = canonicalAgentPath(join21(home, ".codex", "plugins", "cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), bridgeDocument = join21(expected, "SKILL.md");
17601
+ const cache = canonicalAgentPath(join23(home, ".codex", "plugins", "cache"), aliases), read2 = (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), bridgeDocument = join23(expected, "SKILL.md");
17427
17602
  try {
17428
17603
  const claimed = parseCodexNativeHookEnvelope(envelope, bridgeDocument).policy;
17429
17604
  inert = unexpected.filter((entry) => isInertCodexPluginCacheCopy(entry, cache, claimed.allowedHostPaths, read2));
@@ -17464,7 +17639,7 @@ function isSkillsOwnershipMarker(marker) {
17464
17639
  return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
17465
17640
  }
17466
17641
  function hasSkillsOwnershipMarker(dir) {
17467
- const path = join23(dir, SYNC_MARKER_FILE);
17642
+ const path = join25(dir, SYNC_MARKER_FILE);
17468
17643
  try {
17469
17644
  if (!lstatSync20(path).isFile())
17470
17645
  return false;
@@ -17488,9 +17663,9 @@ function resolveSyncAgents(arg) {
17488
17663
  function agentGlobalSkillsDir(agent, homeDir2 = homedir3()) {
17489
17664
  switch (agent) {
17490
17665
  case "opencode":
17491
- return join23(homeDir2, ".config", "opencode", "skills");
17666
+ return join25(homeDir2, ".config", "opencode", "skills");
17492
17667
  default:
17493
- return join23(homeDir2, `.${agent}`, "skills");
17668
+ return join25(homeDir2, `.${agent}`, "skills");
17494
17669
  }
17495
17670
  }
17496
17671
  function adaptSkillMdForAgent(skillMd, agent) {
@@ -17549,7 +17724,7 @@ function resolveSyncCorpus(options = {}) {
17549
17724
  function packageSourceRoots(source) {
17550
17725
  const roots = [];
17551
17726
  for (const sub of ["skills"]) {
17552
- const candidate = join23(source, sub);
17727
+ const candidate = join25(source, sub);
17553
17728
  if (existsSync10(candidate) && isDirectory(candidate))
17554
17729
  roots.push(candidate);
17555
17730
  }
@@ -17565,10 +17740,10 @@ function containsSkillDirectories(path) {
17565
17740
  return false;
17566
17741
  }
17567
17742
  return entries.some((entry) => {
17568
- const candidate = join23(path, entry);
17743
+ const candidate = join25(path, entry);
17569
17744
  if (!isDirectory(candidate))
17570
17745
  return false;
17571
- return existsSync10(join23(candidate, "SKILL.md")) || existsSync10(join23(candidate, "skill.json")) || existsSync10(join23(candidate, "package.json"));
17746
+ return existsSync10(join25(candidate, "SKILL.md")) || existsSync10(join25(candidate, "skill.json")) || existsSync10(join25(candidate, "package.json"));
17572
17747
  });
17573
17748
  }
17574
17749
  function isDirectory(path) {
@@ -17608,7 +17783,7 @@ function syncSkillsToAgents(options = {}) {
17608
17783
  actions.push({
17609
17784
  skill: name,
17610
17785
  agent,
17611
- path: join23(agentGlobalSkillsDir(agent, homeDir2), name, "SKILL.md"),
17786
+ path: join25(agentGlobalSkillsDir(agent, homeDir2), name, "SKILL.md"),
17612
17787
  action: "skip",
17613
17788
  reason: "not found in this machine's corpus"
17614
17789
  });
@@ -17640,7 +17815,7 @@ function syncSkillsToAgents(options = {}) {
17640
17815
  }
17641
17816
  function writeManagedAgentSkill(params) {
17642
17817
  const homeDir2 = params.homeDir ?? homedir3();
17643
- const dir = join23(agentGlobalSkillsDir(params.agent, homeDir2), params.skill);
17818
+ const dir = join25(agentGlobalSkillsDir(params.agent, homeDir2), params.skill);
17644
17819
  const result = writeManagedSkillDir(dir, params.skillMd, {
17645
17820
  skill: params.skill,
17646
17821
  source: params.source,
@@ -17663,8 +17838,8 @@ function writeManagedSkillDir(dir, skillMd, options) {
17663
17838
  return withCodexCorpusWrite(root ? [root] : [], (assertCurrent) => writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent), options);
17664
17839
  }
17665
17840
  function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
17666
- const skillMdPath = join23(dir, "SKILL.md");
17667
- const markerPath = join23(dir, SYNC_MARKER_FILE);
17841
+ const skillMdPath = join25(dir, "SKILL.md");
17842
+ const markerPath = join25(dir, SYNC_MARKER_FILE);
17668
17843
  const dirExists = existsSync10(dir);
17669
17844
  const managed = hasSkillsOwnershipMarker(dir);
17670
17845
  const hasSkillMd = existsSync10(skillMdPath);
@@ -17702,11 +17877,11 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
17702
17877
  return { action, path: skillMdPath };
17703
17878
  const parentDir = dirname15(dir);
17704
17879
  mkdirSync7(parentDir, { recursive: true });
17705
- const transactionDir = mkdtempSync(join23(parentDir, `.hasna-skills-write-${basename7(dir)}-`));
17706
- const candidateDir = join23(transactionDir, "candidate");
17707
- const backupDir = join23(transactionDir, "backup");
17708
- const candidateSkillMdPath = join23(candidateDir, "SKILL.md");
17709
- const candidateMarkerPath = join23(candidateDir, SYNC_MARKER_FILE);
17880
+ const transactionDir = mkdtempSync(join25(parentDir, `.hasna-skills-write-${basename7(dir)}-`));
17881
+ const candidateDir = join25(transactionDir, "candidate");
17882
+ const backupDir = join25(transactionDir, "backup");
17883
+ const candidateSkillMdPath = join25(candidateDir, "SKILL.md");
17884
+ const candidateMarkerPath = join25(candidateDir, SYNC_MARKER_FILE);
17710
17885
  const marker = {
17711
17886
  managedBy: SYNC_MARKER_MANAGED_BY,
17712
17887
  skill: options.skill,
@@ -17764,12 +17939,12 @@ function writeManagedSkillDirUnlocked(dir, skillMd, options, assertCurrent) {
17764
17939
  return { action, path: skillMdPath };
17765
17940
  }
17766
17941
  function removeManagedAgentSkill(skill, agent, homeDir2 = homedir3(), options = {}) {
17767
- const dir = join23(agentGlobalSkillsDir(agent, homeDir2), skill);
17942
+ const dir = join25(agentGlobalSkillsDir(agent, homeDir2), skill);
17768
17943
  const root = codexCorpusRootForPath(dir);
17769
17944
  return withCodexCorpusWrite(root ? [root] : [], () => removeManagedAgentSkillUnlocked(skill, agent, homeDir2, options), options);
17770
17945
  }
17771
17946
  function removeManagedAgentSkillUnlocked(skill, agent, homeDir2 = homedir3(), options = {}) {
17772
- const dir = join23(agentGlobalSkillsDir(agent, homeDir2), skill);
17947
+ const dir = join25(agentGlobalSkillsDir(agent, homeDir2), skill);
17773
17948
  if (!hasSkillsOwnershipMarker(dir))
17774
17949
  return false;
17775
17950
  rmSync(dir, { recursive: true, force: true });
@@ -17777,7 +17952,7 @@ function removeManagedAgentSkillUnlocked(skill, agent, homeDir2 = homedir3(), op
17777
17952
  }
17778
17953
  function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = false) {
17779
17954
  if (kind === undefined || kind === "instruction" || preferBundledDocs) {
17780
- const skillMdPath = join23(skillPath, "SKILL.md");
17955
+ const skillMdPath = join25(skillPath, "SKILL.md");
17781
17956
  if (existsSync10(skillMdPath))
17782
17957
  return readFileSync15(skillMdPath, "utf-8");
17783
17958
  }
@@ -17820,7 +17995,7 @@ function resolveSkillAlias(name) {
17820
17995
 
17821
17996
  // src/lib/project-state.ts
17822
17997
  import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "fs";
17823
- import { join as join25 } from "path";
17998
+ import { join as join26 } from "path";
17824
17999
  var VALID_PIN_SOURCES = [
17825
18000
  "official",
17826
18001
  "custom",
@@ -17835,10 +18010,10 @@ var SKILLS_PROJECT_DIR = ".skills";
17835
18010
  var PROJECT_CONFIG_FILE = "project.json";
17836
18011
  var DEFAULT_EXPORT_DIR = ".skills/exports";
17837
18012
  function getProjectStateDir(targetDir = process.cwd()) {
17838
- return join25(targetDir, SKILLS_PROJECT_DIR);
18013
+ return join26(targetDir, SKILLS_PROJECT_DIR);
17839
18014
  }
17840
18015
  function getProjectConfigPath(targetDir = process.cwd()) {
17841
- return join25(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
18016
+ return join26(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
17842
18017
  }
17843
18018
  function loadProjectConfig(targetDir = process.cwd()) {
17844
18019
  const path = getProjectConfigPath(targetDir);
@@ -17967,7 +18142,7 @@ function getSkillPath(name) {
17967
18142
  const extensionPath = findExtensionSkillPath(skillName);
17968
18143
  if (extensionPath)
17969
18144
  return extensionPath;
17970
- return join26(getPortableSkillsRoot(), skillName);
18145
+ return join27(getPortableSkillsRoot(), skillName);
17971
18146
  }
17972
18147
  function getCanonicalSkillName(name) {
17973
18148
  return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
@@ -18030,7 +18205,7 @@ function createLocalSkillManifest(name, generateSkillMd) {
18030
18205
  if (!existsSync12(sourcePath))
18031
18206
  return null;
18032
18207
  let skillMd = "";
18033
- const skillMdPath = join26(sourcePath, "SKILL.md");
18208
+ const skillMdPath = join27(sourcePath, "SKILL.md");
18034
18209
  if (existsSync12(skillMdPath)) {
18035
18210
  skillMd = readFileSync17(skillMdPath, "utf-8");
18036
18211
  } else if (generateSkillMd) {
@@ -18104,16 +18279,16 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
18104
18279
  const base = projectDir || process.cwd();
18105
18280
  switch (agent) {
18106
18281
  case "pi":
18107
- return scope === "project" ? join26(base, ".pi", "skills") : join26(homedir4(), ".pi", "agent", "skills");
18282
+ return scope === "project" ? join27(base, ".pi", "skills") : join27(homedir4(), ".pi", "agent", "skills");
18108
18283
  case "opencode":
18109
- return scope === "project" ? join26(base, ".opencode", "skills") : join26(homedir4(), ".config", "opencode", "skills");
18284
+ return scope === "project" ? join27(base, ".opencode", "skills") : join27(homedir4(), ".config", "opencode", "skills");
18110
18285
  default:
18111
- return scope === "project" ? join26(base, `.${agent}`, "skills") : join26(homedir4(), `.${agent}`, "skills");
18286
+ return scope === "project" ? join27(base, `.${agent}`, "skills") : join27(homedir4(), `.${agent}`, "skills");
18112
18287
  }
18113
18288
  }
18114
18289
  function getAgentSkillPath(name, agent, scope = "global", projectDir) {
18115
18290
  const skillName = normalizeSkillName(getCanonicalSkillName(name));
18116
- return join26(getAgentSkillsDir(agent, scope, projectDir), skillName);
18291
+ return join27(getAgentSkillsDir(agent, scope, projectDir), skillName);
18117
18292
  }
18118
18293
  function installSkillForAgent(name, options, generateSkillMd) {
18119
18294
  assertNativeExportAllowed();
@@ -18156,7 +18331,7 @@ function removeSkillForAgentUnlocked(name, options) {
18156
18331
  }
18157
18332
  function resolveAgentSkillMd(name, generateSkillMd) {
18158
18333
  const sourcePath = getSkillPath(name);
18159
- const skillMdPath = join26(sourcePath, "SKILL.md");
18334
+ const skillMdPath = join27(sourcePath, "SKILL.md");
18160
18335
  if (existsSync12(skillMdPath))
18161
18336
  return readFileSync17(skillMdPath, "utf-8");
18162
18337
  if (generateSkillMd)
@@ -18191,7 +18366,7 @@ function generateMinimalSkillMd(name) {
18191
18366
  "---",
18192
18367
  ""
18193
18368
  ].filter(Boolean);
18194
- const fallbackDoc = readFileIfExists(join26(sourcePath, "README.md")) || readFileIfExists(join26(sourcePath, "CLAUDE.md"));
18369
+ const fallbackDoc = readFileIfExists(join27(sourcePath, "README.md")) || readFileIfExists(join27(sourcePath, "CLAUDE.md"));
18195
18370
  if (fallbackDoc)
18196
18371
  return `${frontmatter.join(`
18197
18372
  `)}${fallbackDoc.trim()}
@@ -18210,7 +18385,7 @@ skills run ${canonicalName}
18210
18385
  `;
18211
18386
  }
18212
18387
  function readBundledSkillVersion(name) {
18213
- const pkgPath = join26(getSkillPath(name), "package.json");
18388
+ const pkgPath = join27(getSkillPath(name), "package.json");
18214
18389
  if (!existsSync12(pkgPath))
18215
18390
  return "unknown";
18216
18391
  try {
@@ -18265,7 +18440,7 @@ function selectSkillDoc(docs, file) {
18265
18440
  function isInstructionSkillDir(skillPath, meta) {
18266
18441
  if (meta?.kind === "instruction")
18267
18442
  return true;
18268
- const skillMdPath = join27(skillPath, "SKILL.md");
18443
+ const skillMdPath = join28(skillPath, "SKILL.md");
18269
18444
  if (!existsSync13(skillMdPath))
18270
18445
  return false;
18271
18446
  try {
@@ -18295,9 +18470,9 @@ function getSkillDocs(name) {
18295
18470
  if (!existsSync13(skillPath))
18296
18471
  return null;
18297
18472
  return {
18298
- skillMd: readIfExists(join27(skillPath, "SKILL.md")),
18299
- readme: readIfExists(join27(skillPath, "README.md")),
18300
- claudeMd: readIfExists(join27(skillPath, "CLAUDE.md"))
18473
+ skillMd: readIfExists(join28(skillPath, "SKILL.md")),
18474
+ readme: readIfExists(join28(skillPath, "README.md")),
18475
+ claudeMd: readIfExists(join28(skillPath, "CLAUDE.md"))
18301
18476
  };
18302
18477
  }
18303
18478
  function getSkillBestDoc(name, file) {
@@ -18313,14 +18488,14 @@ function getSkillRequirements(name) {
18313
18488
  return null;
18314
18489
  const texts = [];
18315
18490
  for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
18316
- const content = readIfExists(join27(skillPath, file));
18491
+ const content = readIfExists(join28(skillPath, file));
18317
18492
  if (content)
18318
18493
  texts.push(content);
18319
18494
  }
18320
18495
  const meta = getSkill(name);
18321
18496
  let dependencies = {};
18322
18497
  try {
18323
- dependencies = JSON.parse(readFileSync18(join27(skillPath, "package.json"), "utf8")).dependencies || {};
18498
+ dependencies = JSON.parse(readFileSync18(join28(skillPath, "package.json"), "utf8")).dependencies || {};
18324
18499
  } catch {}
18325
18500
  return getSkillRequirementsFromContent(name, texts, dependencies, meta);
18326
18501
  }
@@ -18382,7 +18557,7 @@ async function runSkill(name, args, options = {}) {
18382
18557
  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'.`
18383
18558
  };
18384
18559
  }
18385
- const pkgPath = join27(skillPath, "package.json");
18560
+ const pkgPath = join28(skillPath, "package.json");
18386
18561
  if (!existsSync13(pkgPath)) {
18387
18562
  return { exitCode: 1, error: `No package.json in skill '${name}'` };
18388
18563
  }
@@ -18402,12 +18577,12 @@ async function runSkill(name, args, options = {}) {
18402
18577
  } catch {
18403
18578
  return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
18404
18579
  }
18405
- const entryPath = join27(skillPath, entryPoint2);
18580
+ const entryPath = join28(skillPath, entryPoint2);
18406
18581
  if (!existsSync13(entryPath)) {
18407
18582
  return { exitCode: 1, error: `Entry point '${entryPoint2}' not found in skill '${name}'` };
18408
18583
  }
18409
18584
  const env = { ...process.env, ...options.env };
18410
- if (!existsSync13(join27(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
18585
+ if (!existsSync13(join28(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
18411
18586
  const failure = await prepareSkillDependencies(skillPath, env, options.preparationTimeoutMs);
18412
18587
  if (failure)
18413
18588
  return failure;
@@ -18485,10 +18660,10 @@ function generateSkillMd(name) {
18485
18660
  "---"
18486
18661
  ].join(`
18487
18662
  `);
18488
- const readme = readIfExists(join27(skillPath, "README.md"));
18489
- const claudeMd = readIfExists(join27(skillPath, "CLAUDE.md"));
18663
+ const readme = readIfExists(join28(skillPath, "README.md"));
18664
+ const claudeMd = readIfExists(join28(skillPath, "CLAUDE.md"));
18490
18665
  let cliCommand = null;
18491
- const pkgPath = join27(skillPath, "package.json");
18666
+ const pkgPath = join28(skillPath, "package.json");
18492
18667
  if (existsSync13(pkgPath)) {
18493
18668
  try {
18494
18669
  const pkg = JSON.parse(readFileSync18(pkgPath, "utf-8"));
@@ -18694,7 +18869,7 @@ function exactProfileSelection(spec, profile) {
18694
18869
  }
18695
18870
 
18696
18871
  // src/lib/plugin-projection.ts
18697
- import { createHash as createHash18 } from "crypto";
18872
+ import { createHash as createHash19 } from "crypto";
18698
18873
  import { posix } from "path";
18699
18874
  var PLUGIN_PROJECTION_MANIFEST = "plugin-projection.json";
18700
18875
  var PLUGIN_PROJECTION_LIMITS = Object.freeze({ files: 1024, bytes: 64 * 1024 * 1024, fileBytes: 16 * 1024 * 1024, metadataBytes: 512 * 1024, registrations: 64, history: 128 });
@@ -18705,7 +18880,7 @@ function pluginNeed(value, message) {
18705
18880
  if (!value)
18706
18881
  pluginRefusal(message);
18707
18882
  }
18708
- var pluginHash = (bytes) => createHash18("sha256").update(bytes).digest("hex");
18883
+ var pluginHash = (bytes) => createHash19("sha256").update(bytes).digest("hex");
18709
18884
  function pluginObject(value) {
18710
18885
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
18711
18886
  }
@@ -18721,7 +18896,7 @@ function pluginId(value) {
18721
18896
  function pluginDigest(value) {
18722
18897
  pluginNeed(typeof value === "string" && /^sha256:[a-f0-9]{64}$/.test(value), "Invalid plugin tree digest");
18723
18898
  }
18724
- function parse4(bytes) {
18899
+ function parse5(bytes) {
18725
18900
  pluginNeed(bytes.byteLength <= PLUGIN_PROJECTION_LIMITS.metadataBytes, "Plugin metadata exceeds its limit");
18726
18901
  try {
18727
18902
  return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
@@ -18812,7 +18987,7 @@ function buildPluginProjection(entries) {
18812
18987
  pluginFileWitnesses(entries);
18813
18988
  const contract = entries.filter((item) => item.path === PLUGIN_PROJECTION_MANIFEST);
18814
18989
  pluginNeed(contract.length === 1, "The selected bundle has no unique plugin projection manifest");
18815
- const manifest = parse4(contract[0].bytes);
18990
+ const manifest = parse5(contract[0].bytes);
18816
18991
  validatePluginManifest(manifest);
18817
18992
  const original = entries.filter((item) => item.path.startsWith("original/")).map((item) => ({ ...item, path: item.path.slice(9) }));
18818
18993
  pluginNeed(!original.some((item) => [".in_use", ".orphaned_at", ".gcs-sha", ".links_materialized"].some((path) => within(item.path, path))), "Original plugin packages cannot supply native runtime metadata");
@@ -18820,7 +18995,7 @@ function buildPluginProjection(entries) {
18820
18995
  pluginNeed(originalTreeDigest === manifest.upstream.treeDigest, "The original plugin package differs from its immutable provenance");
18821
18996
  const native = original.find((item) => item.path === ".claude-plugin/plugin.json");
18822
18997
  pluginNeed(native, "The original plugin manifest is missing");
18823
- const config = parse4(native.bytes);
18998
+ const config = parse5(native.bytes);
18824
18999
  pluginKeys(config, MANIFEST_FIELDS);
18825
19000
  pluginNeed(config.name === manifest.pluginId.split("@")[0] && (manifest.upstream.version === null ? !Object.hasOwn(config, "version") : config.version === manifest.upstream.version), "Original plugin identity differs from its contract");
18826
19001
  const skillRoots = ["skills", ...declaredPaths(config.skills)], commandRoots = ["commands", ...declaredPaths(config.commands)];
@@ -18864,7 +19039,7 @@ function buildPluginProjection(entries) {
18864
19039
  if (item.path === native.path)
18865
19040
  continue;
18866
19041
  if (item.path.toLowerCase() === "package.json") {
18867
- const pkg = parse4(item.bytes);
19042
+ const pkg = parse5(item.bytes);
18868
19043
  pluginNeed(pluginObject(pkg), "Invalid plugin package metadata");
18869
19044
  for (const key of ["scripts", "dependencies", "optionalDependencies", "devDependencies", "peerDependencies", "workspaces", "bundledDependencies", "bundleDependencies", "packageManager"])
18870
19045
  pluginNeed(pkg[key] === undefined, "Upstream package installation is outside plugin admission");
@@ -18893,9 +19068,9 @@ function buildPluginProjection(entries) {
18893
19068
  }
18894
19069
 
18895
19070
  // src/lib/plugin-projection-store.ts
18896
- import { createHash as createHash19, randomUUID as randomUUID4 } from "crypto";
19071
+ import { createHash as createHash20, randomUUID as randomUUID4 } from "crypto";
18897
19072
  import { constants as constants11, closeSync as closeSync11, fchmodSync, fstatSync as fstatSync11, fsyncSync as fsyncSync5, linkSync as linkSync3, lstatSync as lstatSync21, mkdirSync as mkdirSync9, openSync as openSync11, opendirSync as opendirSync4, readSync as readSync9, renameSync as renameSync5, rmdirSync as rmdirSync3, rmSync as rmSync3, unlinkSync as unlinkSync5, writeSync as writeSync2 } from "fs";
18898
- import { dirname as dirname16, isAbsolute as isAbsolute14, join as join28, parse as parse5, resolve as resolve14 } from "path";
19073
+ import { dirname as dirname16, isAbsolute as isAbsolute14, join as join29, parse as parse6, resolve as resolve14 } from "path";
18899
19074
  var READ_FLAGS = constants11.O_RDONLY | constants11.O_NOFOLLOW | constants11.O_NONBLOCK;
18900
19075
  var WRITE_FLAGS = constants11.O_WRONLY | constants11.O_CREAT | constants11.O_EXCL | constants11.O_NOFOLLOW | constants11.O_NONBLOCK;
18901
19076
  var uid = () => typeof process.getuid === "function" ? BigInt(process.getuid()) : undefined;
@@ -18909,7 +19084,7 @@ function stat(path) {
18909
19084
  return pluginRefusal("Cannot inspect plugin filesystem entry");
18910
19085
  }
18911
19086
  }
18912
- function canonical5(path) {
19087
+ function canonical6(path) {
18913
19088
  pluginNeed(typeof path === "string" && path.length <= 8192 && isAbsolute14(path) && resolve14(path) === path && !/[\x00-\x1f\x7f]/.test(path), "Plugin storage paths must be canonical absolute paths");
18914
19089
  }
18915
19090
  function owner(stats, system = false) {
@@ -18920,12 +19095,12 @@ function same3(left, right, identityOnly = false) {
18920
19095
  pluginNeed(right && left.dev === right.dev && left.ino === right.ino && left.mode === right.mode && left.uid === right.uid && left.gid === right.gid && (identityOnly || left.size === right.size && left.nlink === right.nlink && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs), "Plugin filesystem entry changed during access");
18921
19096
  }
18922
19097
  function ancestors(path, create = false) {
18923
- canonical5(path);
18924
- const result = [], parent = dirname16(path), filesystemRoot = parse5(parent).root;
19098
+ canonical6(path);
19099
+ const result = [], parent = dirname16(path), filesystemRoot = parse6(parent).root;
18925
19100
  let current = filesystemRoot;
18926
19101
  for (const segment of ["", ...parent.slice(filesystemRoot.length).split("/").filter(Boolean)]) {
18927
19102
  if (segment)
18928
- current = join28(current, segment);
19103
+ current = join29(current, segment);
18929
19104
  let currentStat = stat(current);
18930
19105
  if (!currentStat && create) {
18931
19106
  try {
@@ -19040,7 +19215,7 @@ function snapshotPluginTree(root, options = {}) {
19040
19215
  pluginNeed(++visited <= PLUGIN_PROJECTION_LIMITS.files * 50, "Plugin tree membership exceeds its limit");
19041
19216
  const path = relative7 ? `${relative7}/${name}` : name;
19042
19217
  pluginNeed(new TextEncoder().encode(path).byteLength <= 100 && !/[\\:\x00-\x1f\x7f]/u.test(path), "Plugin entry path exceeds its limit or is unsafe");
19043
- const absolute = join28(directory, name), entryStat = stat(absolute);
19218
+ const absolute = join29(directory, name), entryStat = stat(absolute);
19044
19219
  pluginNeed(entryStat, "Plugin tree member disappeared");
19045
19220
  if (relative7 === "" && name === ".orphaned_at" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
19046
19221
  const bytes2 = readBytes(absolute, 32, "native-pin");
@@ -19057,7 +19232,7 @@ function snapshotPluginTree(root, options = {}) {
19057
19232
  directories.push({ path: absolute, stat: entryStat, names: pins });
19058
19233
  for (const pin of pins) {
19059
19234
  pluginNeed(/^[1-9][0-9]{0,9}$/.test(pin) && Number(pin) <= 2147483647, "Unrecognized native plugin pin name");
19060
- const pinPath = join28(absolute, pin), pinStat = stat(pinPath);
19235
+ const pinPath = join29(absolute, pin), pinStat = stat(pinPath);
19061
19236
  pluginNeed(pinStat, "Native plugin pin disappeared");
19062
19237
  const bytes2 = readBytes(pinPath, 4096, "native-pin");
19063
19238
  let value;
@@ -19155,7 +19330,7 @@ function materializePluginTree(root, entries) {
19155
19330
  mkdirSync9(staging, { mode: 448 });
19156
19331
  staged = stat(staging);
19157
19332
  for (const entry of entries) {
19158
- const path = join28(staging, entry.path);
19333
+ const path = join29(staging, entry.path);
19159
19334
  ancestors(path, true);
19160
19335
  writeExclusive(path, entry.bytes, entry.mode);
19161
19336
  }
@@ -19235,7 +19410,7 @@ function pluginExecutableDigest(path) {
19235
19410
  const opened = fstatSync11(fd, { bigint: true });
19236
19411
  same3(before, opened);
19237
19412
  fileShape(opened, maximum, "executable");
19238
- const hash2 = createHash19("sha256"), buffer = new Uint8Array(64 * 1024);
19413
+ const hash2 = createHash20("sha256"), buffer = new Uint8Array(64 * 1024);
19239
19414
  let remaining = Number(opened.size);
19240
19415
  while (remaining > 0) {
19241
19416
  const count = readSync9(fd, buffer, 0, Math.min(buffer.byteLength, remaining), null);
@@ -19282,7 +19457,7 @@ function describeEntries(entries) {
19282
19457
 
19283
19458
  // src/lib/plugin-admission.ts
19284
19459
  function pluginAdmissionRoot() {
19285
- return join29(homedir5(), ".hasna", "skills", "plugin-admission");
19460
+ return join30(homedir5(), ".hasna", "skills", "plugin-admission");
19286
19461
  }
19287
19462
  function validatePluginTarget(value) {
19288
19463
  pluginKeys(value, ["schemaVersion", "pluginId", "registrations", "native", "resolver"]);
@@ -19458,12 +19633,12 @@ function root(options) {
19458
19633
  }
19459
19634
  function pluginBindingPath(storeRoot, id) {
19460
19635
  pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
19461
- return join29(storeRoot, "bindings", `${id}.json`);
19636
+ return join30(storeRoot, "bindings", `${id}.json`);
19462
19637
  }
19463
19638
  function pluginReceiptPath(storeRoot, id, digest) {
19464
19639
  pluginNeed(/^[a-f0-9]{64}$/.test(id), "Invalid plugin binding identifier");
19465
19640
  pluginDigest(digest);
19466
- return join29(storeRoot, "receipts", id, `${digest.slice(7)}.json`);
19641
+ return join30(storeRoot, "receipts", id, `${digest.slice(7)}.json`);
19467
19642
  }
19468
19643
  function readPluginBinding(storeRoot, id) {
19469
19644
  const value = readPluginJson(pluginBindingPath(storeRoot, id));
@@ -19483,7 +19658,7 @@ function readPluginAdmissionReceipt(storeRoot, id, digest) {
19483
19658
  pluginNeed(receipt.plan.bindingId === id && receipt.plan.planDigest === digest, "Plugin receipt identity differs from its filename");
19484
19659
  const binding = readPluginBinding(storeRoot, id);
19485
19660
  pluginNeed(canonicalJson(binding) === canonicalJson(receipt.plan.binding) && receipt.plan.sourceCommand === pluginResolverCommand(binding), "Plugin receipt binding changed");
19486
- pluginNeed(receipt.materializedPath === join29(storeRoot, "objects", id, digest.slice(7)), "Plugin receipt points outside its immutable materialization");
19661
+ pluginNeed(receipt.materializedPath === join30(storeRoot, "objects", id, digest.slice(7)), "Plugin receipt points outside its immutable materialization");
19487
19662
  pluginText(receipt.admittedAt, 64);
19488
19663
  pluginNeed(Number.isFinite(Date.parse(receipt.admittedAt)), "Invalid plugin admission time");
19489
19664
  return receipt;
@@ -19624,7 +19799,7 @@ async function admitPlugin(spec, profileId, target, approvedDigest, approvedEvid
19624
19799
  const { plan, projection } = await candidate(spec, profileId, target, options);
19625
19800
  pluginNeed(plan.planDigest === approvedDigest, "The plugin plan changed since review; obtain and review a new plan");
19626
19801
  pluginNeed(plan.evidenceDigest === approvedEvidenceDigest, "The plugin routing evidence changed since review; obtain and review a new plan");
19627
- const store = root(options), materializedPath = join29(store, "objects", plan.bindingId, plan.planDigest.slice(7));
19802
+ const store = root(options), materializedPath = join30(store, "objects", plan.bindingId, plan.planDigest.slice(7));
19628
19803
  materializePluginTree(materializedPath, projection.files);
19629
19804
  writePluginJsonImmutable(pluginBindingPath(store, plan.bindingId), plan.binding);
19630
19805
  const path = pluginReceiptPath(store, plan.bindingId, plan.planDigest), existing = readPluginJson(path);
@@ -19716,7 +19891,7 @@ function hashManagedPluginRegistry(path, managed) {
19716
19891
  seen.add(target.pluginId);
19717
19892
  const rows = plugins[target.pluginId];
19718
19893
  pluginNeed(Array.isArray(rows) && rows.length === target.registrations.length, "Managed plugin registration membership changed");
19719
- const [name, market] = target.pluginId.split("@"), cacheRoot = join30(dirname17(path), "cache", market, name);
19894
+ const [name, market] = target.pluginId.split("@"), cacheRoot = join31(dirname17(path), "cache", market, name);
19720
19895
  const scopes = new Set, approvals = new Map, currentFiles = new Map;
19721
19896
  plugins[target.pluginId] = rows.map((row) => {
19722
19897
  pluginNeed(pluginObject(row), "Invalid managed plugin registration");
@@ -19729,7 +19904,7 @@ function hashManagedPluginRegistry(path, managed) {
19729
19904
  pluginText(row.sourceProducerPath);
19730
19905
  pluginText(row.lastUpdated, 64);
19731
19906
  pluginNeed(Number.isFinite(Date.parse(row.lastUpdated)), "Invalid managed plugin update timestamp");
19732
- pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join30(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
19907
+ pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join31(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
19733
19908
  const approval = (producer) => {
19734
19909
  pluginText(producer);
19735
19910
  const digest = `sha256:${basename8(producer)}`;
@@ -19772,11 +19947,11 @@ function hashManagedPluginRegistry(path, managed) {
19772
19947
  directory.closeSync();
19773
19948
  }
19774
19949
  for (const version of names.sort()) {
19775
- const files = pluginFileWitnesses(snapshotPluginTree(join30(cacheRoot, version), { nativeRuntime: `claude-${target.native.version}` }));
19950
+ const files = pluginFileWitnesses(snapshotPluginTree(join31(cacheRoot, version), { nativeRuntime: `claude-${target.native.version}` }));
19776
19951
  verifiedBytes += files.reduce((total, file) => total + file.size, 0);
19777
19952
  pluginNeed(verifiedBytes <= 256 * 1024 * 1024, "Managed plugin verification exceeds its aggregate byte budget");
19778
19953
  const serialized = JSON.stringify(files);
19779
- const expected = currentFiles.get(join30(cacheRoot, version));
19954
+ const expected = currentFiles.get(join31(cacheRoot, version));
19780
19955
  pluginNeed(expected === undefined || serialized === expected, "Current native cache differs from its exact registered producer receipt");
19781
19956
  pluginNeed([...approvals.values()].some((receipt) => nativeVersionMatches(version, receipt) && JSON.stringify(receipt.plan.files) === serialized), "A retained native plugin cache version differs from every approved projection");
19782
19957
  }
@@ -19794,7 +19969,7 @@ function captureManagedPluginRegistry(path, managedPlugins) {
19794
19969
  }
19795
19970
 
19796
19971
  // src/lib/claude-marketplace-registry.ts
19797
- import { createHash as createHash20 } from "crypto";
19972
+ import { createHash as createHash21 } from "crypto";
19798
19973
  import { closeSync as closeSync13, constants as constants13, fstatSync as fstatSync13, lstatSync as lstatSync23, openSync as openSync13, readSync as readSync11 } from "fs";
19799
19974
  import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute17, resolve as resolve17 } from "path";
19800
19975
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
@@ -19851,7 +20026,7 @@ function readRegistry2(path, budget2) {
19851
20026
  closeSync13(fd);
19852
20027
  }
19853
20028
  }
19854
- function parse6(text2) {
20029
+ function parse7(text2) {
19855
20030
  let at = 0, nodes = 0;
19856
20031
  const numberPattern = /-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/y;
19857
20032
  const space = () => {
@@ -19960,22 +20135,22 @@ function normalizeRow(row, version = 1) {
19960
20135
  row.entries = row.entries.map(([key, value]) => [key, key === "lastUpdated" ? { kind: "validated-update-time" } : value]);
19961
20136
  }
19962
20137
  function captureClaudeMarketplaceRegistry(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
19963
- const value = parse6(readRegistry2(path, budget2));
20138
+ const value = parse7(readRegistry2(path, budget2));
19964
20139
  for (const [, row] of value.entries)
19965
20140
  normalizeRow(row);
19966
- const sha2562 = createHash20("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
20141
+ const sha2562 = createHash21("sha256").update("hasna.skills.claude-marketplace-registry.v1\x00").update(JSON.stringify(value)).digest("hex");
19967
20142
  return { path, hashMode: "claude-marketplace-registry", sha256: sha2562 };
19968
20143
  }
19969
20144
  function captureClaudeMarketplaceRegistryV2(path, budget2 = { remaining: 256 * 1024 * 1024 }) {
19970
- const value = parse6(readRegistry2(path, budget2));
20145
+ const value = parse7(readRegistry2(path, budget2));
19971
20146
  for (const [, row] of value.entries)
19972
20147
  normalizeRow(row, 2);
19973
- const sha2562 = createHash20("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
20148
+ const sha2562 = createHash21("sha256").update("hasna.skills.claude-marketplace-registry.v2\x00").update(JSON.stringify(value)).digest("hex");
19974
20149
  return { path, hashMode: "claude-marketplace-registry-v2", sha256: sha2562 };
19975
20150
  }
19976
20151
 
19977
20152
  // src/lib/codex-hook-discovery-recovery.ts
19978
- import { createHash as createHash21 } from "crypto";
20153
+ import { createHash as createHash22 } from "crypto";
19979
20154
 
19980
20155
  // src/lib/codex-hook-additions-review.ts
19981
20156
  var events = { UserPromptSubmit: ["userPromptSubmit", "user_prompt_submit"], SessionStart: ["sessionStart", "session_start"], SubagentStart: ["subagentStart", "subagent_start"] };
@@ -19983,7 +20158,7 @@ var matchedEvents = { ...events, PreToolUse: ["preToolUse", "pre_tool_use"] };
19983
20158
  var proofs = new WeakMap;
19984
20159
 
19985
20160
  // src/lib/codex-hook-discovery-recovery.ts
19986
- var sha4 = (text2) => createHash21("sha256").update(text2).digest("hex");
20161
+ var sha4 = (text2) => createHash22("sha256").update(text2).digest("hex");
19987
20162
  var fields = [...CODEX_DISCOVERY_PROJECTION_FIELDS];
19988
20163
  var proofs2 = new WeakMap;
19989
20164
  function assertCodexHookDiscoveryRecovery(binding, witness) {
@@ -20002,7 +20177,7 @@ function verifiesCodexHookDiscoverySource(binding, source, currentDigest, witnes
20002
20177
  }
20003
20178
 
20004
20179
  // src/lib/agent-discovery.ts
20005
- var digest = (text2) => createHash22("sha256").update(text2).digest("hex");
20180
+ var digest = (text2) => createHash23("sha256").update(text2).digest("hex");
20006
20181
  function parseConfig(text2, path, toml = false) {
20007
20182
  try {
20008
20183
  const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
@@ -20088,6 +20263,12 @@ function projected(source, changes, budget2 = discoveryByteBudget()) {
20088
20263
  throw new Error("Managed plugin registry witnesses require verified native registrations");
20089
20264
  return hashManagedPluginRegistry(source.path, source.managedPlugins);
20090
20265
  }
20266
+ if (source.hashMode === "claude-plugin-manifest-v1") {
20267
+ if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || changes?.has(source.path))
20268
+ throw new Error("Claude plugin manifest witnesses require a native discovery source without synthetic changes");
20269
+ const text3 = read2(source.path);
20270
+ return text3 === null ? null : hashClaudePluginManifest(text3);
20271
+ }
20091
20272
  if (source.managedPlugins !== undefined)
20092
20273
  throw new Error("Managed plugin rules require a typed registry witness");
20093
20274
  if (source.hashMode !== undefined) {
@@ -20114,7 +20295,7 @@ function absentCodexSkillRoots(binding) {
20114
20295
  throw new Error("Invalid disabled Codex discovery proof");
20115
20296
  if (binding.method !== "reviewed" || controls.every((control) => lstatSync24(control.pluginParent, { throwIfNoEntry: false })))
20116
20297
  return [];
20117
- const cache = dirname19(dirname19(controls[0].pluginParent)), config = join31(dirname19(dirname19(cache)), "config.toml");
20298
+ const cache = dirname19(dirname19(controls[0].pluginParent)), config = join32(dirname19(dirname19(cache)), "config.toml");
20118
20299
  const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
20119
20300
  if (!source || projected(source) !== source.sha256)
20120
20301
  throw new Error("Missing current disabled Codex configuration witness");
@@ -20122,8 +20303,8 @@ function absentCodexSkillRoots(binding) {
20122
20303
  return [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && absentDisabledCodexPluginParent(cache, parent, controls, rules));
20123
20304
  }
20124
20305
  function codexDirectHookSelects(cache, root2) {
20125
- const codex = dirname19(dirname19(cache)), config = Bun.TOML.parse(read2(join31(codex, "config.toml")) ?? "");
20126
- const hooks = read2(join31(codex, "hooks.json"));
20306
+ const codex = dirname19(dirname19(cache)), config = Bun.TOML.parse(read2(join32(codex, "config.toml")) ?? "");
20307
+ const hooks = read2(join32(codex, "hooks.json"));
20127
20308
  if (hooks !== null)
20128
20309
  hashNativeJsonControls(hooks);
20129
20310
  return JSON.stringify(config.hooks ?? {}).includes(root2) || hooks !== null && JSON.stringify(JSON.parse(hooks)).includes(root2);
@@ -20135,7 +20316,7 @@ function retiredCodexRoots(binding) {
20135
20316
  const controls = binding.codexDisabledPluginSkills;
20136
20317
  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)
20137
20318
  throw new Error("Invalid retired Codex materialization proof");
20138
- const cache = dirname19(dirname19(controls[0].pluginParent)), config = join31(dirname19(dirname19(cache)), "config.toml");
20319
+ const cache = dirname19(dirname19(controls[0].pluginParent)), config = join32(dirname19(dirname19(cache)), "config.toml");
20139
20320
  const source = binding.sources.find((source2) => source2.path === config && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
20140
20321
  if (!source || projected(source) !== source.sha256)
20141
20322
  throw new Error("Missing current retired Codex configuration witness");
@@ -20143,7 +20324,7 @@ function retiredCodexRoots(binding) {
20143
20324
  if ((proof.parents ?? []).some((parent) => codexDirectHookSelects(cache, parent) || !reviewedDisabledCodexPluginParent(cache, parent, controls, rules)) || !reviewedCodexPluginCapabilitiesUnchanged(cache, controls, (path) => read2(path), rules))
20144
20325
  throw new Error("Retired Codex parent capabilities changed");
20145
20326
  for (const root2 of proof.roots) {
20146
- const manifest = binding.sources.find((source2) => source2.path === join31(root2, ".codex-plugin/plugin.json") && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && [undefined, "bytes"].includes(source2.hashMode));
20327
+ const manifest = binding.sources.find((source2) => source2.path === join32(root2, ".codex-plugin/plugin.json") && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && [undefined, "bytes"].includes(source2.hashMode));
20147
20328
  if (!manifest || codexDirectHookSelects(cache, root2) || !reviewedRetiredCodexRoot(cache, root2, controls, rules, (path) => read2(path), true))
20148
20329
  throw new Error("Retired Codex materialization changed");
20149
20330
  }
@@ -20153,14 +20334,14 @@ function captureRetiredCodexDiscovery(binding, controls, rules) {
20153
20334
  if (binding.agent !== "codex" || binding.method !== "reviewed" || !controls.length)
20154
20335
  return binding;
20155
20336
  const cache = dirname19(dirname19(controls[0].pluginParent));
20156
- const candidates = binding.sources.filter((source) => source.path.endsWith(sep7 + ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined && [undefined, "bytes"].includes(source.hashMode)).map((source) => dirname19(dirname19(source.path)));
20337
+ const candidates = binding.sources.filter((source) => source.path.endsWith(sep8 + ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined && [undefined, "bytes"].includes(source.hashMode)).map((source) => dirname19(dirname19(source.path)));
20157
20338
  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();
20158
20339
  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() : [];
20159
20340
  const { codexRetiredMaterializations: previous, ...current } = binding;
20160
20341
  if (!roots.length && !parents3.length)
20161
20342
  return current;
20162
20343
  const installation = codexInstallationRoots(binding);
20163
- const directories = (binding.directories ?? []).filter((directory) => !installation.some((root2) => directory.path === root2 || directory.path.startsWith(root2 + sep7) || root2.startsWith(directory.path + sep7)));
20344
+ const directories = (binding.directories ?? []).filter((directory) => !installation.some((root2) => directory.path === root2 || directory.path.startsWith(root2 + sep8) || root2.startsWith(directory.path + sep8)));
20164
20345
  if (previous && isDeepStrictEqual7(previous.roots, roots) && isDeepStrictEqual7(previous.parents, parents3) && isDeepStrictEqual7(previous.directories.map((item) => item.path), directories.map((item) => item.path)))
20165
20346
  return { ...current, codexRetiredMaterializations: previous };
20166
20347
  return { ...current, codexRetiredMaterializations: { roots, parents: parents3, directories: captureDiscoveryDirectoryProjection(directories.map((item) => item.path), roots) } };
@@ -20172,17 +20353,17 @@ function absentRetiredClaudeRoots(binding) {
20172
20353
  for (const source of binding.sources) {
20173
20354
  if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || ![undefined, "bytes"].includes(source.hashMode) || source.sha256 === null)
20174
20355
  continue;
20175
- const marker = sep7 + ".claude" + sep7 + "plugins" + sep7 + "cache" + sep7, at = source.path.indexOf(marker);
20356
+ const marker = sep8 + ".claude" + sep8 + "plugins" + sep8 + "cache" + sep8, at = source.path.indexOf(marker);
20176
20357
  if (at < 0 || source.path.indexOf(marker, at + 1) >= 0)
20177
20358
  continue;
20178
- const cache = source.path.slice(0, at + marker.length - 1), parts = source.path.slice(at + marker.length).split(sep7);
20359
+ const cache = source.path.slice(0, at + marker.length - 1), parts = source.path.slice(at + marker.length).split(sep8);
20179
20360
  if (parts.length < 4 || parts.slice(0, 3).some((part) => !part || part === "." || part === ".."))
20180
20361
  continue;
20181
- const root2 = join31(cache, ...parts.slice(0, 3));
20362
+ const root2 = join32(cache, ...parts.slice(0, 3));
20182
20363
  safe4(root2);
20183
20364
  if (lstatSync24(root2, { throwIfNoEntry: false }))
20184
20365
  continue;
20185
- const claude = dirname19(dirname19(cache)), settings = join31(claude, "settings.json"), registry = join31(claude, "plugins/installed_plugins.json");
20366
+ const claude = dirname19(dirname19(cache)), settings = join32(claude, "settings.json"), registry = join32(claude, "plugins/installed_plugins.json");
20186
20367
  const full = (path) => binding.sources.find((item) => item.path === path && item.sha256 !== null && item.format === undefined && item.fields === undefined);
20187
20368
  const configSource = full(settings), registrySource = full(registry);
20188
20369
  if (!configSource || !registrySource || projected(configSource) !== configSource.sha256 || projected(registrySource) !== registrySource.sha256)
@@ -20201,10 +20382,10 @@ function absentRetiredClaudeRoots(binding) {
20201
20382
  const active = rows[0].installPath;
20202
20383
  if (rows[0].version !== basename10(active) || resolve18(active) !== active || dirname19(active) !== dirname19(root2) || active === root2 || !lstatSync24(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync10(active) !== active)
20203
20384
  continue;
20204
- const activeManifest = full(join31(active, ".claude-plugin/plugin.json"));
20385
+ const activeManifest = full(join32(active, ".claude-plugin/plugin.json"));
20205
20386
  if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
20206
20387
  continue;
20207
- 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 + sep7) || root2.startsWith(row.installPath + sep7))))
20388
+ 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 + sep8) || root2.startsWith(row.installPath + sep8))))
20208
20389
  continue;
20209
20390
  result.add(root2);
20210
20391
  }
@@ -20219,10 +20400,10 @@ function verifyAgentDiscovery(binding, codexRecovery) {
20219
20400
  throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
20220
20401
  const installationRoots = codexInstallationRoots(binding);
20221
20402
  const retiredRoots = retiredCodexRoots(binding);
20222
- const installationInput = (path) => installationRoots.some((root2) => path === root2 || path.startsWith(root2 + sep7));
20403
+ const installationInput = (path) => installationRoots.some((root2) => path === root2 || path.startsWith(root2 + sep8));
20223
20404
  if (binding.directories !== undefined) {
20224
20405
  const projected2 = binding.codexInstallationInputs?.directories ?? [];
20225
- const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root2) => root2.startsWith(directory.path + sep7)));
20406
+ const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root2) => root2.startsWith(directory.path + sep8)));
20226
20407
  if (projected2.length !== ancestors2.length || projected2.some((directory) => !ancestors2.some((original) => original.path === directory.path)))
20227
20408
  throw new Error("Missing installation input directory projection");
20228
20409
  const ordinary = binding.directories.filter((directory) => !installationInput(directory.path) && !ancestors2.includes(directory));
@@ -20258,7 +20439,7 @@ function verifyAgentDiscovery(binding, codexRecovery) {
20258
20439
  safe4(source.path);
20259
20440
  continue;
20260
20441
  }
20261
- if (absentRoots.some((root2) => source.path.startsWith(root2 + sep7)) && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && [undefined, "bytes"].includes(source.hashMode)) {
20442
+ if (absentRoots.some((root2) => source.path.startsWith(root2 + sep8)) && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && [undefined, "bytes"].includes(source.hashMode)) {
20262
20443
  safe4(source.path);
20263
20444
  continue;
20264
20445
  }
@@ -20281,12 +20462,12 @@ function codexInstallationRoots(binding) {
20281
20462
  if (binding.agent !== "codex" || binding.method !== "reviewed" || proof.version !== "codex-cli 0.160.0" || !/^[a-f0-9]{64}$/.test(proof.catalogSha256) || !Array.isArray(proof.plugins) || !proof.plugins.length)
20282
20463
  throw new Error("Invalid native installation input proof");
20283
20464
  const cache = dirname19(dirname19(proof.plugins[0].pluginParent));
20284
- const config = join31(dirname19(dirname19(cache)), "config.toml"), home = dirname19(dirname19(dirname19(cache)));
20465
+ const config = join32(dirname19(dirname19(cache)), "config.toml"), home = dirname19(dirname19(dirname19(cache)));
20285
20466
  const configSource = binding.sources.find((source) => source.path === config && source.sha256 !== null && (!source.format || source.format === "toml" && source.fields?.includes("plugins")));
20286
- const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join31(home, path));
20287
- if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep7) || input.sourceRoot.startsWith(root2 + sep7)) || binding.sources.some((source) => basename10(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep7)))))
20467
+ const nativeRoots = NATIVE_SKILL_ROOTS.filter(([agent]) => agent === "codex").map(([, path]) => join32(home, path));
20468
+ if (!configSource || proof.plugins.some((input) => nativeRoots.some((root2) => root2 === input.sourceRoot || root2.startsWith(input.sourceRoot + sep8) || input.sourceRoot.startsWith(root2 + sep8)) || binding.sources.some((source) => basename10(source.path) === "config.toml" && (source.path === input.sourceRoot || source.path.startsWith(input.sourceRoot + sep8)))))
20288
20469
  throw new Error("Native installation input overlaps configuration or loading roots");
20289
- if (proof.plugins.some((input) => dirname19(dirname19(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join31(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
20470
+ if (proof.plugins.some((input) => dirname19(dirname19(input.pluginParent)) !== cache || !binding.sources.some((source) => source.path === join32(input.sourceRoot, ".codex-plugin/plugin.json") && source.sha256 !== null && source.format === undefined && source.fields === undefined)))
20290
20471
  throw new Error("Missing native installation input source witness");
20291
20472
  return reviewedCodexPluginSourceRoots(cache, proof.plugins, (path) => {
20292
20473
  const text2 = read2(path);
@@ -20296,6 +20477,8 @@ function codexInstallationRoots(binding) {
20296
20477
  });
20297
20478
  }
20298
20479
  function assertMarketplaceBinding(binding, source) {
20480
+ if (source.hashMode === "claude-plugin-manifest-v1" && (binding.agent !== "claude" || basename10(source.path) !== "plugin.json" || !source.path.endsWith(join32(sep8, ".claude-plugin", "plugin.json"))))
20481
+ throw new Error("Claude plugin manifest witnesses require Claude plugin discovery");
20299
20482
  if (source.hashMode === "sumi-settings-v1" && (binding.agent !== "sumi" || binding.method !== "reviewed" || basename10(source.path) !== "sumi.json"))
20300
20483
  throw new Error("Sumi settings witnesses require explicit reviewed Sumi configuration");
20301
20484
  if ((source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")) && (binding.agent !== "codex" || binding.method !== "reviewed" || basename10(source.path) !== "config.toml"))
@@ -20310,7 +20493,7 @@ function rebindAgentDiscovery(binding, changes) {
20310
20493
  const installationRoots = codexInstallationRoots(binding), absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredCodexRoots(binding).filter((root2) => !lstatSync24(root2, { throwIfNoEntry: false }))];
20311
20494
  const rebound = { ...binding, sources: binding.sources.map((source) => {
20312
20495
  assertMarketplaceBinding(binding, source);
20313
- if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep7)) || absentRoots.some((root2) => source.path.startsWith(root2 + sep7)) && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && [undefined, "bytes"].includes(source.hashMode)))
20496
+ if (!changes.has(source.path) && (installationRoots.some((root2) => source.path.startsWith(root2 + sep8)) || absentRoots.some((root2) => source.path.startsWith(root2 + sep8)) && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && [undefined, "bytes"].includes(source.hashMode)))
20314
20497
  return source;
20315
20498
  const sha2562 = projected(source, changes, budget2);
20316
20499
  if (!changes.has(source.path) && sha2562 !== source.sha256)
@@ -20336,8 +20519,8 @@ function captureDiscoveryPathSources(paths) {
20336
20519
  const budget2 = discoveryByteBudget();
20337
20520
  return paths.map((path) => ({ path, hashMode: "path-bytes", sha256: hashDiscoveryPathFile(path, budget2) }));
20338
20521
  }
20339
- function assertProjectDiscovery(agent, directories, home, canonical6 = resolve18, reviewed) {
20340
- const homeConfigPath = canonical6(agentDiscoveryConfigPath(home, agent));
20522
+ function assertProjectDiscovery(agent, directories, home, canonical7 = resolve18, reviewed) {
20523
+ const homeConfigPath = canonical7(agentDiscoveryConfigPath(home, agent));
20341
20524
  for (const directory of directories) {
20342
20525
  const isHome = resolve18(directory) === resolve18(home);
20343
20526
  if (agent === "hermes") {
@@ -20348,7 +20531,7 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve18
20348
20531
  continue;
20349
20532
  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"];
20350
20533
  for (const suffix of paths) {
20351
- const path = canonical6(join31(directory, suffix)), raw = read2(path);
20534
+ const path = canonical7(join32(directory, suffix)), raw = read2(path);
20352
20535
  if (raw === null)
20353
20536
  continue;
20354
20537
  const isRootAliasedHomeConfig = agent === "claude" && suffix === ".claude/settings.json" || agent === "codex" && suffix === ".codex/config.toml";
@@ -20364,7 +20547,7 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve18
20364
20547
  const managed = reviewed.sources.flatMap((source) => source.hashMode === "claude-plugin-registry" ? source.managedPlugins ?? [] : []);
20365
20548
  if (exact && projected(exact) === exact.sha256 && managed.length) {
20366
20549
  const bindings = managed.map((item) => readPluginBinding(item.storeRoot, item.bindingId));
20367
- admittedPlugins = Object.entries(config.enabledPlugins).every(([id, enabled]) => typeof enabled === "boolean" && bindings.some((binding) => binding.target.pluginId === id && binding.target.registrations.some((scope) => scope.scope === "project" && scope.projectPath === canonical6(directory))));
20550
+ admittedPlugins = Object.entries(config.enabledPlugins).every(([id, enabled]) => typeof enabled === "boolean" && bindings.some((binding) => binding.target.pluginId === id && binding.target.registrations.some((scope) => scope.scope === "project" && scope.projectPath === canonical7(directory))));
20368
20551
  }
20369
20552
  }
20370
20553
  if (keys.some((key) => config[key] !== undefined && !(key === "enabledPlugins" && admittedPlugins)) || config.disableAllHooks === true || config.disableBundledSkills === false || config.hooksConfig?.enabled === false || config.permission?.skill !== undefined || config.permissions?.deny?.some((rule) => typeof rule === "string" && /^Skill(?:\(|$)/.test(rule)))
@@ -20372,13 +20555,13 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve18
20372
20555
  }
20373
20556
  if (agent === "sumi")
20374
20557
  for (const name of ["plugin", "plugins"]) {
20375
- const path = canonical6(join31(directory, ".sumi", name));
20558
+ const path = canonical7(join32(directory, ".sumi", name));
20376
20559
  safe4(path);
20377
20560
  if (lstatSync24(path, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(path).length)
20378
20561
  throw new Error(`NATIVE_SKILL_DRIFT: project Sumi plugins require a dedicated discovery review: ${path}`);
20379
20562
  }
20380
20563
  if (agent === "claude") {
20381
- const commands = canonical6(join31(directory, ".claude/commands"));
20564
+ const commands = canonical7(join32(directory, ".claude/commands"));
20382
20565
  safe4(commands);
20383
20566
  if (lstatSync24(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
20384
20567
  throw new Error("NATIVE_SKILL_DRIFT: legacy project command discovery requires a dedicated format adapter");
@@ -20388,13 +20571,13 @@ function assertProjectDiscovery(agent, directories, home, canonical6 = resolve18
20388
20571
  function agentDiscoveryConfigPath(home, agent) {
20389
20572
  if (agent === "sumi")
20390
20573
  return sumiConfigPath(home);
20391
- return join31(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
20574
+ return join32(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
20392
20575
  }
20393
20576
  function resolveAgentDiscovery(options) {
20394
- const canonical6 = options.canonical ?? resolve18, home = resolve18(options.home), agent = options.agent;
20577
+ const canonical7 = options.canonical ?? resolve18, home = resolve18(options.home), agent = options.agent;
20395
20578
  const sources = [], roots = new Set, builtinNames = [];
20396
- const witness = (path, format, fields2) => {
20397
- const source = { path: canonical6(path), sha256: null, ...format ? { format, fields: fields2 } : {} };
20579
+ const witness = (path, format, fields2, hashMode) => {
20580
+ const source = { path: canonical7(path), sha256: null, ...format ? { format, fields: fields2 } : {}, ...hashMode ? { hashMode } : {} };
20398
20581
  source.sha256 = projected(source);
20399
20582
  sources.push(source);
20400
20583
  return read2(source.path);
@@ -20407,31 +20590,34 @@ function resolveAgentDiscovery(options) {
20407
20590
  const unresolved = (detail) => {
20408
20591
  throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
20409
20592
  };
20593
+ if (options.reviewed && (options.reviewed.version !== 1 || !Array.isArray(options.reviewed.agents) || options.reviewed.agents.some((item) => !item || typeof item !== "object") || new Set(options.reviewed.agents.map((item) => item.agent)).size !== options.reviewed.agents.length))
20594
+ throw new Error("Invalid --discovery-inputs version or agents");
20595
+ const review = options.reviewed?.agents.find((item) => item.agent === agent);
20410
20596
  if (agent === "sumi") {
20411
20597
  const directory = sumiConfigDirectory(home);
20412
- if (witness(join31(directory, "sumi.jsonc")) !== null)
20598
+ if (witness(join32(directory, "sumi.jsonc")) !== null)
20413
20599
  unresolved("JSONC configuration requires a supported format adapter");
20414
- for (const root2 of [directory, join31(home, ".claude"), join31(home, ".agents")])
20600
+ for (const root2 of [directory, join32(home, ".claude"), join32(home, ".agents")])
20415
20601
  for (const name of ["skill", "skills"])
20416
- roots.add(canonical6(join31(root2, name)));
20602
+ roots.add(canonical7(join32(root2, name)));
20417
20603
  if (config.skills !== undefined && (!Array.isArray(config.skills) || config.skills.some((path) => typeof path !== "string")))
20418
20604
  unresolved("skills must be a string array");
20419
20605
  for (const value of config.skills ?? []) {
20420
20606
  if (typeof value !== "string" || /[\0$]/.test(value) || !value || !(isAbsolute18(value) || value.startsWith("~/")))
20421
20607
  unresolved("remote or relative skill sources need a dedicated discovery adapter");
20422
- roots.add(canonical6(value.startsWith("~/") ? join31(home, value.slice(2)) : value));
20608
+ roots.add(canonical7(value.startsWith("~/") ? join32(home, value.slice(2)) : value));
20423
20609
  }
20424
20610
  for (const key of ["plugin", "plugins"])
20425
20611
  if (config[key] !== undefined && !Array.isArray(config[key]))
20426
20612
  unresolved(`${key} must be an array`);
20427
- const plugins = [join31(directory, "plugin"), join31(directory, "plugins")];
20613
+ const plugins = [join32(directory, "plugin"), join32(directory, "plugins")];
20428
20614
  for (const path of plugins) {
20429
20615
  safe4(path);
20430
20616
  if (lstatSync24(path, { throwIfNoEntry: false })?.isDirectory())
20431
20617
  for (const name of readdirSync10(path).sort()) {
20432
20618
  if (name === "skills-cli.js" && path === plugins[1])
20433
20619
  continue;
20434
- const target = join31(path, name);
20620
+ const target = join32(path, name);
20435
20621
  if (!lstatSync24(target).isFile())
20436
20622
  unresolved("local plugin packages need a dedicated discovery adapter");
20437
20623
  witness(target);
@@ -20444,7 +20630,7 @@ function resolveAgentDiscovery(options) {
20444
20630
  }
20445
20631
  }
20446
20632
  if (agent === "hermes") {
20447
- const profile = witness(join31(home, ".hermes/active_profile"));
20633
+ const profile = witness(join32(home, ".hermes/active_profile"));
20448
20634
  if (profile?.trim() && profile.trim() !== "default")
20449
20635
  unresolved("a non-default profile is active");
20450
20636
  const declared = config.skills?.external_dirs ?? [], paths = typeof declared === "string" ? [declared] : declared;
@@ -20458,7 +20644,7 @@ function resolveAgentDiscovery(options) {
20458
20644
  continue;
20459
20645
  if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
20460
20646
  unresolved("user-specific tilde expansion requires a separate discovery adapter");
20461
- const path = normalized === "~" ? home : normalized.startsWith("~/") ? join31(home, normalized.slice(2)) : resolve18(home, ".hermes", normalized);
20647
+ const path = normalized === "~" ? home : normalized.startsWith("~/") ? join32(home, normalized.slice(2)) : resolve18(home, ".hermes", normalized);
20462
20648
  safe4(path);
20463
20649
  roots.add(path);
20464
20650
  }
@@ -20468,7 +20654,7 @@ function resolveAgentDiscovery(options) {
20468
20654
  if (executable2) {
20469
20655
  let packageRoot;
20470
20656
  for (let directory = dirname19(realpathSync10(executable2)), depth = 0;depth < 8; directory = dirname19(directory), depth++) {
20471
- const path = join31(directory, "package.json"), raw = read2(path);
20657
+ const path = join32(directory, "package.json"), raw = read2(path);
20472
20658
  if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
20473
20659
  packageRoot = directory;
20474
20660
  witness(path);
@@ -20479,12 +20665,12 @@ function resolveAgentDiscovery(options) {
20479
20665
  }
20480
20666
  if (!packageRoot)
20481
20667
  unresolved("installed Gemini package layout is not recognized");
20482
- const builtinRoot = join31(packageRoot, "bundle/builtin");
20668
+ const builtinRoot = join32(packageRoot, "bundle/builtin");
20483
20669
  safe4(builtinRoot);
20484
20670
  if (!lstatSync24(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
20485
20671
  unresolved("installed Gemini builtin root is missing");
20486
20672
  for (const name of readdirSync10(builtinRoot).sort()) {
20487
- const text2 = witness(join31(builtinRoot, name, "SKILL.md"));
20673
+ const text2 = witness(join32(builtinRoot, name, "SKILL.md"));
20488
20674
  const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
20489
20675
  if (!match?.[1] || builtinNames.includes(match[1]))
20490
20676
  unresolved("installed Gemini builtin name is missing or ambiguous");
@@ -20497,18 +20683,15 @@ function resolveAgentDiscovery(options) {
20497
20683
  if (retained.agent !== agent || retained.method !== "reviewed")
20498
20684
  unresolved("invalid retained review");
20499
20685
  verifyAgentDiscovery(retained);
20500
- if (!retained.sources.some((source) => source.path === canonical6(configPath) && source.format === undefined && source.fields === undefined))
20686
+ if (!retained.sources.some((source) => source.path === canonical7(configPath) && source.format === undefined && source.fields === undefined))
20501
20687
  unresolved("retained review is missing the full agent configuration source");
20502
- if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
20688
+ if (retained.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
20503
20689
  unresolved("retained settings witness names another configuration source");
20504
- const covered = supersedesSettingsProjection(agent, retained.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical6(configPath), source)) : sources;
20690
+ const covered = supersedesSettingsProjection(agent, retained.sources, canonical7(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical7(configPath), source)) : sources;
20505
20691
  if (covered.some((source) => !retained.sources.some((saved) => isDeepStrictEqual7(saved, source))) || [...roots].some((root2) => !retained.roots.includes(root2)) || agent === "gemini" && JSON.stringify(retained.builtinNames) !== JSON.stringify(builtinNames))
20506
20692
  unresolved("retained review is missing current runtime discovery coverage");
20507
20693
  return retained;
20508
20694
  }
20509
- if (options.reviewed && (options.reviewed.version !== 1 || !Array.isArray(options.reviewed.agents) || options.reviewed.agents.some((item) => !item || typeof item !== "object") || new Set(options.reviewed.agents.map((item) => item.agent)).size !== options.reviewed.agents.length))
20510
- throw new Error("Invalid --discovery-inputs version or agents");
20511
- const review = options.reviewed?.agents.find((item) => item.agent === agent);
20512
20695
  if (review) {
20513
20696
  if (review.pluginHooks !== "reviewed-no-skill-injection" || !Array.isArray(review.sources) || !review.sources.length || !Array.isArray(review.roots))
20514
20697
  throw new Error("Discovery review must bind sources and confirm plugin hooks do not inject retired skills");
@@ -20516,47 +20699,77 @@ function resolveAgentDiscovery(options) {
20516
20699
  if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
20517
20700
  throw new Error("Explicit discovery reviews require full source-file hashes");
20518
20701
  verifyAgentDiscovery(supplied);
20519
- if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical6(configPath)))
20702
+ if (review.sources.some((source) => (source.hashMode === "sumi-settings-v1" || source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4")))) && source.path !== canonical7(configPath)))
20520
20703
  throw new Error("Semantic settings witnesses must name the configured agent configuration source");
20521
- if (!review.sources.some((source) => source.path === canonical6(configPath)))
20704
+ if (!review.sources.some((source) => source.path === canonical7(configPath)))
20522
20705
  throw new Error("Discovery review must include the agent configuration source");
20523
- const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical6(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical6(configPath), source)) : sources;
20524
- return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: [...reviewedSources, ...review.sources], ...agent === "gemini" ? { builtinNames } : {} };
20706
+ const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical7(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical7(configPath), source)) : sources;
20707
+ const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
20708
+ if (agent !== "claude" || source.sha256 === null || source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || source.hashMode !== undefined && source.hashMode !== "bytes" || basename10(source.path) !== "plugin.json" || !source.path.endsWith(join32(sep8, ".claude-plugin", "plugin.json")))
20709
+ return source;
20710
+ const pluginRoot = dirname19(dirname19(source.path)), hooksPath = join32(pluginRoot, "hooks/hooks.json");
20711
+ const hookStat = lstatSync24(hooksPath, { throwIfNoEntry: false });
20712
+ const text2 = read2(source.path);
20713
+ if (text2 === null)
20714
+ throw new Error("Reviewed Claude plugin manifest is missing");
20715
+ const manifestSha256 = projectReviewedClaudePluginManifest(text2, source.sha256 ?? "");
20716
+ const manifest = JSON.parse(text2);
20717
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
20718
+ throw new Error("Reviewed Claude plugin manifest must be an object");
20719
+ const reviewedHookPaths = new Set;
20720
+ if (hookStat)
20721
+ reviewedHookPaths.add(hooksPath);
20722
+ if (manifest.hooks !== undefined) {
20723
+ if (typeof manifest.hooks !== "string")
20724
+ throw new Error("Reviewed Claude plugin manifest has an unsupported hooks target");
20725
+ const declaredHookPath = resolve18(pluginRoot, manifest.hooks);
20726
+ if (!declaredHookPath.startsWith(pluginRoot + sep8))
20727
+ throw new Error("Reviewed Claude plugin hooks target escapes its root");
20728
+ reviewedHookPaths.add(declaredHookPath);
20729
+ }
20730
+ for (const hooksFile of reviewedHookPaths)
20731
+ if (!review.sources.some((hook) => hook.path === canonical7(hooksFile) && hook.sha256 !== null && hook.format === undefined && hook.fields === undefined && hook.managedPlugins === undefined && (hook.hashMode === undefined || hook.hashMode === "bytes")))
20732
+ throw new Error("Reviewed Claude plugin manifest requires separately reviewed exact hook file sources");
20733
+ return { path: source.path, sha256: manifestSha256, hashMode: "claude-plugin-manifest-v1" };
20734
+ });
20735
+ const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
20736
+ const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual7(candidate2, source)) === index);
20737
+ return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
20525
20738
  }
20526
20739
  function plugin(root2) {
20527
- root2 = canonical6(root2);
20740
+ root2 = canonical7(root2);
20528
20741
  safe4(root2);
20529
- const manifestPath = join31(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
20530
- const raw = witness(manifestPath);
20742
+ const manifestPath = join32(root2, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
20743
+ const raw = witness(manifestPath, undefined, undefined, agent === "claude" ? "claude-plugin-manifest-v1" : undefined);
20531
20744
  if (raw === null)
20532
20745
  unresolved("plugin manifest missing");
20533
20746
  const manifest = parseConfig(raw, manifestPath);
20534
- const hooks = witness(join31(root2, "hooks/hooks.json"));
20747
+ const hooks = witness(join32(root2, "hooks/hooks.json"));
20535
20748
  if (manifest.hooks !== undefined || hooks !== null)
20536
20749
  unresolved("plugin hooks require a separate no-skill-injection review");
20537
- const commands = join31(root2, "commands");
20750
+ const commands = join32(root2, "commands");
20538
20751
  safe4(commands);
20539
20752
  if (agent === "claude" && (manifest.commands !== undefined || lstatSync24(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length))
20540
20753
  unresolved("legacy command discovery requires retirement or a dedicated format adapter");
20541
- const rootSkill = witness(join31(root2, "SKILL.md"));
20754
+ const rootSkill = witness(join32(root2, "SKILL.md"));
20542
20755
  if (rootSkill !== null)
20543
20756
  roots.add(root2);
20544
20757
  const declared = manifest.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
20545
20758
  if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
20546
20759
  unresolved("unsupported plugin skill paths");
20547
20760
  if (agent === "claude")
20548
- roots.add(join31(root2, "skills"));
20761
+ roots.add(join32(root2, "skills"));
20549
20762
  for (const value of declared) {
20550
20763
  const path = resolve18(root2, value);
20551
- if (path !== root2 && !path.startsWith(root2 + sep7))
20764
+ if (path !== root2 && !path.startsWith(root2 + sep8))
20552
20765
  unresolved("plugin skill path escapes its root");
20553
- roots.add(path.endsWith(`${sep7}SKILL.md`) ? dirname19(path) : path);
20766
+ roots.add(path.endsWith(`${sep8}SKILL.md`) ? dirname19(path) : path);
20554
20767
  }
20555
20768
  }
20556
20769
  if (agent === "claude") {
20557
- const registrations = witness(join31(home, ".claude/plugins/installed_plugins.json"));
20558
- const installed = registrations === null ? {} : parseConfig(registrations, join31(home, ".claude/plugins/installed_plugins.json")).plugins;
20559
- const commands = canonical6(join31(home, ".claude/commands"));
20770
+ const registrations = witness(join32(home, ".claude/plugins/installed_plugins.json"));
20771
+ const installed = registrations === null ? {} : parseConfig(registrations, join32(home, ".claude/plugins/installed_plugins.json")).plugins;
20772
+ const commands = canonical7(join32(home, ".claude/commands"));
20560
20773
  safe4(commands);
20561
20774
  if (lstatSync24(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync10(commands).length)
20562
20775
  unresolved("legacy home command discovery requires a dedicated format adapter");
@@ -20580,16 +20793,16 @@ function resolveAgentDiscovery(options) {
20580
20793
  const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
20581
20794
  if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute18(market.source))
20582
20795
  unresolved("enabled plugin marketplace is not an explicit local source");
20583
- const catalogText = witness(join31(market.source, ".agents/plugins/marketplace.json"));
20796
+ const catalogText = witness(join32(market.source, ".agents/plugins/marketplace.json"));
20584
20797
  if (catalogText === null)
20585
20798
  unresolved("marketplace catalog missing");
20586
- const catalog = parseConfig(catalogText, join31(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
20799
+ const catalog = parseConfig(catalogText, join32(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
20587
20800
  if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
20588
20801
  unresolved("plugin catalog source is missing or ambiguous");
20589
20802
  plugin(resolve18(market.source, matches[0].source.path));
20590
20803
  }
20591
20804
  } else if (agent === "opencode") {
20592
- if (witness(join31(home, ".config/opencode/opencode.jsonc")) !== null || witness(join31(home, ".config/opencode/config.json")) !== null)
20805
+ if (witness(join32(home, ".config/opencode/opencode.jsonc")) !== null || witness(join32(home, ".config/opencode/config.json")) !== null)
20593
20806
  unresolved("additional config layers require review");
20594
20807
  if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
20595
20808
  unresolved("external plugin or remote skill source");
@@ -20598,15 +20811,15 @@ function resolveAgentDiscovery(options) {
20598
20811
  unresolved("relative or malformed added skill root");
20599
20812
  roots.add(path);
20600
20813
  }
20601
- const localPlugins = join31(home, ".config/opencode/plugins");
20814
+ const localPlugins = join32(home, ".config/opencode/plugins");
20602
20815
  safe4(localPlugins);
20603
20816
  if (lstatSync24(localPlugins, { throwIfNoEntry: false }) && readdirSync10(localPlugins).some((name) => name !== "skills-cli.js"))
20604
20817
  unresolved("additional local plugin hooks require review");
20605
20818
  } else if (agent === "sumi") {
20606
- if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical6(configPath) && source.path !== canonical6(join31(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
20819
+ if ((config.plugin?.length ?? 0) > 0 || (config.plugins?.length ?? 0) > 0 || sources.some((source) => source.path !== canonical7(configPath) && source.path !== canonical7(join32(sumiConfigDirectory(home), "sumi.jsonc")) && source.sha256 !== null))
20607
20820
  unresolved("external or foreign local plugins require review");
20608
20821
  } else if (agent === "hermes") {
20609
- for (const directory of [join31(home, ".hermes/plugins"), join31(home, ".hermes/hermes-agent")]) {
20822
+ for (const directory of [join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")]) {
20610
20823
  safe4(directory);
20611
20824
  if (lstatSync24(directory, { throwIfNoEntry: false }))
20612
20825
  unresolved("installed runtime/plugin discovery requires reviewed source bindings");
@@ -20616,30 +20829,30 @@ function resolveAgentDiscovery(options) {
20616
20829
  } else if (agent === "gemini") {
20617
20830
  if (Object.keys(config.extensions ?? {}).length || config.skills?.paths?.length)
20618
20831
  unresolved("custom extension or skill paths");
20619
- const directory = join31(home, ".gemini/extensions");
20832
+ const directory = join32(home, ".gemini/extensions");
20620
20833
  safe4(directory);
20621
20834
  if (lstatSync24(directory, { throwIfNoEntry: false }))
20622
20835
  for (const name of readdirSync10(directory)) {
20623
- const root2 = join31(directory, name);
20836
+ const root2 = join32(directory, name);
20624
20837
  safe4(root2);
20625
20838
  if (!lstatSync24(root2)?.isDirectory())
20626
20839
  continue;
20627
- const raw = witness(join31(root2, "gemini-extension.json"));
20840
+ const raw = witness(join32(root2, "gemini-extension.json"));
20628
20841
  if (raw === null)
20629
20842
  unresolved("extension registration missing");
20630
- const manifest = parseConfig(raw, join31(root2, "gemini-extension.json"));
20631
- if (manifest.hooks !== undefined || witness(join31(root2, "hooks/hooks.json")) !== null || witness(join31(root2, ".gemini-extension-install.json")) !== null)
20843
+ const manifest = parseConfig(raw, join32(root2, "gemini-extension.json"));
20844
+ if (manifest.hooks !== undefined || witness(join32(root2, "hooks/hooks.json")) !== null || witness(join32(root2, ".gemini-extension-install.json")) !== null)
20632
20845
  unresolved("extension hooks or linked registration require review");
20633
- roots.add(join31(root2, "skills"));
20846
+ roots.add(join32(root2, "skills"));
20634
20847
  }
20635
20848
  }
20636
- return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join31(home, ".hermes/plugins"), join31(home, ".hermes/hermes-agent")].map((path) => canonical6(path))) } : {} };
20849
+ return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join32(home, ".hermes/plugins"), join32(home, ".hermes/hermes-agent")].map((path) => canonical7(path))) } : {} };
20637
20850
  }
20638
20851
 
20639
20852
  // src/lib/authoring-path.ts
20640
20853
  var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
20641
20854
  function pathParts(path) {
20642
- return resolve19(path).split(sep8).filter(Boolean);
20855
+ return resolve19(path).split(sep9).filter(Boolean);
20643
20856
  }
20644
20857
  function nativeRootMatch(path) {
20645
20858
  const parts = pathParts(path);
@@ -20665,10 +20878,10 @@ function physicalPath(path) {
20665
20878
  return resolve19(realpathSync11(cursor), ...suffix);
20666
20879
  }
20667
20880
  function contains(root2, path) {
20668
- return path === root2 || path.startsWith(root2 + sep8);
20881
+ return path === root2 || path.startsWith(root2 + sep9);
20669
20882
  }
20670
20883
  function configuredRoots(home, explicitHome) {
20671
- const homeData = join32(home, ".hasna", "skills");
20884
+ const homeData = join33(home, ".hasna", "skills");
20672
20885
  const homePolicy = readManagedSkillPolicySnapshot(homeData);
20673
20886
  const configuredPolicy = !explicitHome && resolve19(getDataDirReadOnly()) !== resolve19(homeData) ? readManagedSkillPolicySnapshot(getDataDirReadOnly()) : null;
20674
20887
  const policy = homePolicy ?? configuredPolicy;
@@ -20699,7 +20912,7 @@ function configuredRoots(home, explicitHome) {
20699
20912
  admitBinding(binding);
20700
20913
  continue;
20701
20914
  }
20702
- const nativeHome = join32(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
20915
+ const nativeHome = join33(home, agent === "opencode" ? ".config/opencode" : `.${agent}`);
20703
20916
  if (!lstatSync25(nativeHome, { throwIfNoEntry: false }))
20704
20917
  continue;
20705
20918
  roots.push(...resolveAgentDiscovery({ home, agent, canonical: physicalPath }).roots);
@@ -20714,8 +20927,8 @@ function assertPortableAuthoringPath(path, options = {}) {
20714
20927
  const candidates = [lexical, physical];
20715
20928
  let native = candidates.map(nativeRootMatch).find(Boolean);
20716
20929
  const home = resolve19(options.homeDir ?? homedir6());
20717
- const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join32(home, ...parts));
20718
- defaults.push(join32(home, ".codex/plugins/cache"), join32(home, ".claude/plugins/cache"), join32(home, ".claude/plugins/synced"), join32(home, ".gemini/extensions"));
20930
+ const defaults = NATIVE_DISCOVERY_ROOTS.map((parts) => join33(home, ...parts));
20931
+ defaults.push(join33(home, ".codex/plugins/cache"), join33(home, ".claude/plugins/cache"), join33(home, ".claude/plugins/synced"), join33(home, ".gemini/extensions"));
20719
20932
  for (const root2 of defaults) {
20720
20933
  if (candidates.some((candidate2) => contains(resolve19(root2), candidate2) || contains(physicalPath(root2), candidate2)))
20721
20934
  native ??= root2;
@@ -20736,15 +20949,15 @@ function getPortableSkillsRoot(options = {}) {
20736
20949
  function resolvePortableSkillsRoot(options, migrate) {
20737
20950
  if (options.rootDir)
20738
20951
  return options.rootDir;
20739
- const appDir = options.homeDir ? join33(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
20740
- const cache = join33(appDir, SKILLS_CACHE_DIRNAME);
20952
+ const appDir = options.homeDir ? join34(options.homeDir, ".hasna", "skills") : migrate ? getDataDir() : getDataDirReadOnly();
20953
+ const cache = join34(appDir, SKILLS_CACHE_DIRNAME);
20741
20954
  if (isOwnerLayoutMigrated(appDir) && safeIsDirectory(cache))
20742
20955
  return cache;
20743
- const installed = join33(appDir, INSTALLED_SKILLS_DIRNAME);
20956
+ const installed = join34(appDir, INSTALLED_SKILLS_DIRNAME);
20744
20957
  return installed;
20745
20958
  }
20746
20959
  function getPortableSkillPath(name, options = {}) {
20747
- return join33(getPortableSkillsRoot(options), normalizePortableSkillName(name));
20960
+ return join34(getPortableSkillsRoot(options), normalizePortableSkillName(name));
20748
20961
  }
20749
20962
  function findPortableSkill(name, options = {}) {
20750
20963
  let normalized;
@@ -20770,7 +20983,7 @@ function listPortableSkills(options = {}) {
20770
20983
  for (const entry of readdirSync11(root2).sort()) {
20771
20984
  if (entry.startsWith("."))
20772
20985
  continue;
20773
- const path = join33(root2, entry);
20986
+ const path = join34(root2, entry);
20774
20987
  if (!safeIsDirectory(path))
20775
20988
  continue;
20776
20989
  try {
@@ -20804,9 +21017,9 @@ function isOfficialSkillName(name) {
20804
21017
  function scaffoldPortableSkill(name, options = {}) {
20805
21018
  const skillName = normalizeNewPortableSkillName(name);
20806
21019
  const readOnlyRoot = resolvePortableSkillsRoot(options, false);
20807
- assertPortableAuthoringPath(join33(readOnlyRoot, skillName), options);
21020
+ assertPortableAuthoringPath(join34(readOnlyRoot, skillName), options);
20808
21021
  const root2 = getPortableSkillsRoot(options);
20809
- const skillPath = join33(root2, skillName);
21022
+ const skillPath = join34(root2, skillName);
20810
21023
  assertPortableAuthoringPath(skillPath, options);
20811
21024
  if (existsSync14(skillPath)) {
20812
21025
  if (!options.overwrite)
@@ -20840,7 +21053,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
20840
21053
  const skipped = [];
20841
21054
  const entries = readdirSync11(absoluteSource, { withFileTypes: true }).map((entry) => entry.name).filter((entryName) => !entryName.startsWith(".")).sort();
20842
21055
  for (const entryName of entries) {
20843
- const childPath = join33(absoluteSource, entryName);
21056
+ const childPath = join34(absoluteSource, entryName);
20844
21057
  if (!safeIsDirectory(childPath))
20845
21058
  continue;
20846
21059
  if (!isSkillCandidate(childPath)) {
@@ -20869,7 +21082,7 @@ function portPortableSkillDirectory(sourceDir, options = {}) {
20869
21082
  };
20870
21083
  }
20871
21084
  function isSkillCandidate(dir) {
20872
- return existsSync14(join33(dir, "SKILL.md")) || existsSync14(join33(dir, "skill.json")) || existsSync14(join33(dir, "package.json"));
21085
+ return existsSync14(join34(dir, "SKILL.md")) || existsSync14(join34(dir, "skill.json")) || existsSync14(join34(dir, "package.json"));
20873
21086
  }
20874
21087
  function physicalImportPath(path) {
20875
21088
  let ancestor = resolve20(path);
@@ -20878,14 +21091,14 @@ function physicalImportPath(path) {
20878
21091
  suffix.unshift(basename12(ancestor));
20879
21092
  ancestor = dirname21(ancestor);
20880
21093
  }
20881
- return join33(realpathSync12(ancestor), ...suffix);
21094
+ return join34(realpathSync12(ancestor), ...suffix);
20882
21095
  }
20883
21096
  function assertDisjointImportPaths(source, destination) {
20884
21097
  const sourcePath = realpathSync12(source);
20885
21098
  const destinationPath = physicalImportPath(destination);
20886
21099
  const contains2 = (parent, child) => {
20887
21100
  const path = relative7(parent, child);
20888
- return path === "" || !isAbsolute19(path) && path !== ".." && !path.startsWith(`..${sep9}`);
21101
+ return path === "" || !isAbsolute19(path) && path !== ".." && !path.startsWith(`..${sep10}`);
20889
21102
  };
20890
21103
  if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
20891
21104
  throw new Error("Source and destination skill directories must not overlap");
@@ -20904,11 +21117,11 @@ function portPortableSkill(sourcePath, options = {}) {
20904
21117
  const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename12(absoluteSource)}')` : ""} matches a bundled official skill.`;
20905
21118
  throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
20906
21119
  }
20907
- const readOnlyDestination = join33(resolvePortableSkillsRoot(options, false), skillName);
21120
+ const readOnlyDestination = join34(resolvePortableSkillsRoot(options, false), skillName);
20908
21121
  assertPortableAuthoringPath(readOnlyDestination, options);
20909
21122
  assertDisjointImportPaths(absoluteSource, readOnlyDestination);
20910
21123
  const root2 = getPortableSkillsRoot(options);
20911
- const destination = join33(root2, skillName);
21124
+ const destination = join34(root2, skillName);
20912
21125
  assertPortableAuthoringPath(destination, options);
20913
21126
  assertDisjointImportPaths(absoluteSource, destination);
20914
21127
  if (existsSync14(destination)) {
@@ -20953,13 +21166,13 @@ function buildCorpusManifest(input, name) {
20953
21166
  function writeCorpusSkill(input, options = {}) {
20954
21167
  const name = normalizePortableSkillName(input.name);
20955
21168
  const readOnlyRoot = resolvePortableSkillsRoot(options, false);
20956
- assertPortableAuthoringPath(join33(readOnlyRoot, name), options);
21169
+ assertPortableAuthoringPath(join34(readOnlyRoot, name), options);
20957
21170
  const root2 = getPortableSkillsRoot(options);
20958
- const skillPath = join33(root2, name);
21171
+ const skillPath = join34(root2, name);
20959
21172
  assertPortableAuthoringPath(skillPath, options);
20960
21173
  const created = !existsSync14(skillPath);
20961
21174
  mkdirSync10(skillPath, { recursive: true });
20962
- writeFileSync9(join33(skillPath, "SKILL.md"), input.skillMd);
21175
+ writeFileSync9(join34(skillPath, "SKILL.md"), input.skillMd);
20963
21176
  const manifest = buildCorpusManifest(input, name);
20964
21177
  writeSkillJsonWithHash(skillPath, manifest);
20965
21178
  return { name, path: skillPath, manifest, created };
@@ -20967,22 +21180,22 @@ function writeCorpusSkill(input, options = {}) {
20967
21180
  function installCorpusSkillAtomically(input, options = {}) {
20968
21181
  const name = normalizePortableSkillName(input.name);
20969
21182
  const readOnlyRoot = resolvePortableSkillsRoot(options, false);
20970
- assertPortableAuthoringPath(join33(readOnlyRoot, name), options);
21183
+ assertPortableAuthoringPath(join34(readOnlyRoot, name), options);
20971
21184
  const root2 = getPortableSkillsRoot(options);
20972
- const target = join33(root2, name);
21185
+ const target = join34(root2, name);
20973
21186
  assertPortableAuthoringPath(target, options);
20974
21187
  const created = !existsSync14(target);
20975
21188
  mkdirSync10(root2, { recursive: true });
20976
- const staging = mkdtempSync2(join33(root2, `.pull-${name}-`));
21189
+ const staging = mkdtempSync2(join34(root2, `.pull-${name}-`));
20977
21190
  let moved = false;
20978
21191
  let backup = null;
20979
21192
  try {
20980
- writeFileSync9(join33(staging, "SKILL.md"), input.skillMd);
21193
+ writeFileSync9(join34(staging, "SKILL.md"), input.skillMd);
20981
21194
  const manifest = buildCorpusManifest(input, name);
20982
21195
  writeSkillJsonWithHash(staging, manifest);
20983
21196
  if (existsSync14(target)) {
20984
- backup = mkdtempSync2(join33(root2, `.pull-backup-${name}-`));
20985
- renameSync6(target, join33(backup, name));
21197
+ backup = mkdtempSync2(join34(root2, `.pull-backup-${name}-`));
21198
+ renameSync6(target, join34(backup, name));
20986
21199
  moved = true;
20987
21200
  }
20988
21201
  renameSync6(staging, target);
@@ -20991,9 +21204,9 @@ function installCorpusSkillAtomically(input, options = {}) {
20991
21204
  return { name, path: target, manifest, created };
20992
21205
  } catch (error) {
20993
21206
  rmSync4(staging, { recursive: true, force: true });
20994
- if (moved && backup && existsSync14(join33(backup, name))) {
21207
+ if (moved && backup && existsSync14(join34(backup, name))) {
20995
21208
  try {
20996
- renameSync6(join33(backup, name), target);
21209
+ renameSync6(join34(backup, name), target);
20997
21210
  } catch {}
20998
21211
  }
20999
21212
  throw error;
@@ -21006,8 +21219,8 @@ function validatePortableSkillDirectory(name, skillPath) {
21006
21219
  const warnings = [...base.warnings];
21007
21220
  let manifest;
21008
21221
  if (existsSync14(skillPath)) {
21009
- const skillJsonPath = join33(skillPath, "skill.json");
21010
- const skillMdPath = join33(skillPath, "SKILL.md");
21222
+ const skillJsonPath = join34(skillPath, "skill.json");
21223
+ const skillMdPath = join34(skillPath, "SKILL.md");
21011
21224
  if (!existsSync14(skillJsonPath) && !existsSync14(skillMdPath)) {
21012
21225
  add3(issues, "portable.manifest_missing", "Missing portable manifest: expected SKILL.md frontmatter and/or skill.json");
21013
21226
  }
@@ -21027,7 +21240,7 @@ function validatePortableSkillDirectory(name, skillPath) {
21027
21240
  add3(issues, "portable.version_missing", "Portable manifest missing version");
21028
21241
  }
21029
21242
  const contractIssues = validatePortableManifestContract(manifest, {
21030
- strict: existsSync14(join33(skillPath, "skill.json")),
21243
+ strict: existsSync14(join34(skillPath, "skill.json")),
21031
21244
  skillPath
21032
21245
  });
21033
21246
  for (const issue of contractIssues)
@@ -21067,7 +21280,7 @@ function validatePortableSkillDirectory(name, skillPath) {
21067
21280
  add3(issues, "portable.command_entry_unsafe", `Command '${command.name}' entry '${command.entry}' must stay inside the skill directory`);
21068
21281
  continue;
21069
21282
  }
21070
- const entryPath = join33(skillPath, command.entry);
21283
+ const entryPath = join34(skillPath, command.entry);
21071
21284
  if (!existsSync14(entryPath))
21072
21285
  add3(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
21073
21286
  else if (statSync11(entryPath).isDirectory())
@@ -21078,7 +21291,7 @@ function validatePortableSkillDirectory(name, skillPath) {
21078
21291
  } catch (error) {
21079
21292
  add3(issues, "portable.manifest_invalid", error.message);
21080
21293
  }
21081
- if (manifest?.kind !== "instruction" && !existsSync14(join33(skillPath, "AGENTS.md"))) {
21294
+ if (manifest?.kind !== "instruction" && !existsSync14(join34(skillPath, "AGENTS.md"))) {
21082
21295
  add3(issues, "portable.agents_missing", "Missing AGENTS.md with build-out instructions for coding agents");
21083
21296
  }
21084
21297
  }
@@ -21114,12 +21327,12 @@ async function runPortableSkill(name, args, options = {}) {
21114
21327
  if (!isSafeRelativePath2(command.entry)) {
21115
21328
  return { exitCode: 1, error: `Portable skill '${name}' command entry is unsafe` };
21116
21329
  }
21117
- const entryPath = join33(skill.path, command.entry);
21330
+ const entryPath = join34(skill.path, command.entry);
21118
21331
  if (!existsSync14(entryPath)) {
21119
21332
  return { exitCode: 1, error: `Entry point '${command.entry}' not found in portable skill '${name}'` };
21120
21333
  }
21121
- const pkgPath = join33(skill.path, "package.json");
21122
- const nodeModules = join33(skill.path, "node_modules");
21334
+ const pkgPath = join34(skill.path, "package.json");
21335
+ const nodeModules = join34(skill.path, "node_modules");
21123
21336
  if (existsSync14(pkgPath) && !existsSync14(nodeModules) && hasPackageDependencies(pkgPath)) {
21124
21337
  const install = Bun.spawn(["bun", "install", "--no-save"], {
21125
21338
  cwd: skill.path,
@@ -21376,7 +21589,7 @@ function discoverSkillsInDir(dir, source = "custom") {
21376
21589
  for (const entry of entries) {
21377
21590
  if (!entry.isDirectory())
21378
21591
  continue;
21379
- const skillMdPath = join34(dir, entry.name, "SKILL.md");
21592
+ const skillMdPath = join35(dir, entry.name, "SKILL.md");
21380
21593
  if (!existsSync15(skillMdPath))
21381
21594
  continue;
21382
21595
  let content;
@@ -21396,7 +21609,7 @@ function discoverSkillsInDir(dir, source = "custom") {
21396
21609
  category: fm.category || "Development Tools",
21397
21610
  tags: fm.tags || [],
21398
21611
  ...fm.kind ? { kind: fm.kind } : {},
21399
- ...isHostedMetadataSkillDir(join34(dir, entry.name)) ? { serverOwned: true } : {},
21612
+ ...isHostedMetadataSkillDir(join35(dir, entry.name)) ? { serverOwned: true } : {},
21400
21613
  source
21401
21614
  });
21402
21615
  }
@@ -21412,8 +21625,8 @@ function findExtensionSkillPath(name) {
21412
21625
  for (const entry of entries) {
21413
21626
  if (!entry.isDirectory())
21414
21627
  continue;
21415
- const skillDir = join34(config.extensionsDir, entry.name);
21416
- const skillMdPath = join34(skillDir, "SKILL.md");
21628
+ const skillDir = join35(config.extensionsDir, entry.name);
21629
+ const skillMdPath = join35(skillDir, "SKILL.md");
21417
21630
  if (!existsSync15(skillMdPath))
21418
21631
  continue;
21419
21632
  let content;
@@ -21482,21 +21695,21 @@ function getAllTags(catalog) {
21482
21695
  return Array.from(tagSet).sort();
21483
21696
  }
21484
21697
  // src/lib/run-state.ts
21485
- import { createHash as createHash23, randomBytes } from "crypto";
21698
+ import { createHash as createHash24, randomBytes } from "crypto";
21486
21699
  import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync21, readdirSync as readdirSync13, statSync as statSync12, writeFileSync as writeFileSync10 } from "fs";
21487
- import { extname, join as join35, relative as relative8 } from "path";
21700
+ import { extname, join as join36, relative as relative8 } from "path";
21488
21701
  function createSkillRun(params, targetDir = process.cwd()) {
21489
21702
  const now = new Date;
21490
21703
  const id = createRunId(now);
21491
21704
  const day = now.toISOString().slice(0, 10);
21492
21705
  const skillName = normalizeSkillName(params.skill);
21493
21706
  const root2 = getProjectStateDir(targetDir);
21494
- const runDir = join35(root2, "runs", day, id);
21495
- const logsDir = join35(runDir, "logs");
21496
- const exportDir = join35(root2, "exports", skillName, id);
21707
+ const runDir = join36(root2, "runs", day, id);
21708
+ const logsDir = join36(runDir, "logs");
21709
+ const exportDir = join36(root2, "exports", skillName, id);
21497
21710
  mkdirSync11(logsDir, { recursive: true });
21498
21711
  mkdirSync11(exportDir, { recursive: true });
21499
- mkdirSync11(join35(root2, "tmp"), { recursive: true });
21712
+ mkdirSync11(join36(root2, "tmp"), { recursive: true });
21500
21713
  const record3 = {
21501
21714
  id,
21502
21715
  skill: skillName,
@@ -21548,27 +21761,27 @@ function updateSkillRun(context, patch) {
21548
21761
  return context.record;
21549
21762
  }
21550
21763
  function writeRunLogs(context, stdout = "", stderr = "") {
21551
- writeFileSync10(join35(context.logsDir, "stdout.log"), stdout);
21552
- writeFileSync10(join35(context.logsDir, "stderr.log"), stderr);
21764
+ writeFileSync10(join36(context.logsDir, "stdout.log"), stdout);
21765
+ writeFileSync10(join36(context.logsDir, "stderr.log"), stderr);
21553
21766
  }
21554
21767
  function appendRunEvent(context, event, data = {}) {
21555
21768
  const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
21556
21769
  `;
21557
- const path = join35(context.runDir, "events.ndjson");
21770
+ const path = join36(context.runDir, "events.ndjson");
21558
21771
  const previous = existsSync16(path) ? readFileSync21(path, "utf-8") : "";
21559
21772
  writeFileSync10(path, previous + line);
21560
21773
  }
21561
21774
  function listSkillRuns(targetDir = process.cwd(), limit = 50) {
21562
- const runsRoot = join35(getProjectStateDir(targetDir), "runs");
21775
+ const runsRoot = join36(getProjectStateDir(targetDir), "runs");
21563
21776
  if (!existsSync16(runsRoot))
21564
21777
  return [];
21565
21778
  const records = [];
21566
21779
  for (const day of readdirSync13(runsRoot).sort().reverse()) {
21567
- const dayDir = join35(runsRoot, day);
21780
+ const dayDir = join36(runsRoot, day);
21568
21781
  if (!statSync12(dayDir).isDirectory())
21569
21782
  continue;
21570
21783
  for (const runId of readdirSync13(dayDir).sort().reverse()) {
21571
- const record3 = readRunRecord(join35(dayDir, runId));
21784
+ const record3 = readRunRecord(join36(dayDir, runId));
21572
21785
  if (record3)
21573
21786
  records.push(record3);
21574
21787
  if (records.length >= limit)
@@ -21578,25 +21791,25 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
21578
21791
  return records;
21579
21792
  }
21580
21793
  function findSkillRun(runId, targetDir = process.cwd()) {
21581
- const runsRoot = join35(getProjectStateDir(targetDir), "runs");
21794
+ const runsRoot = join36(getProjectStateDir(targetDir), "runs");
21582
21795
  if (!existsSync16(runsRoot))
21583
21796
  return null;
21584
21797
  for (const day of readdirSync13(runsRoot)) {
21585
- const record3 = readRunRecord(join35(runsRoot, day, runId));
21798
+ const record3 = readRunRecord(join36(runsRoot, day, runId));
21586
21799
  if (record3)
21587
21800
  return record3;
21588
21801
  }
21589
21802
  return null;
21590
21803
  }
21591
21804
  function getRunExportDir(runId, skill, targetDir = process.cwd()) {
21592
- return join35(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
21805
+ return join36(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
21593
21806
  }
21594
21807
  function writeRunRecord(context) {
21595
- writeFileSync10(join35(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
21808
+ writeFileSync10(join36(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
21596
21809
  `);
21597
21810
  }
21598
21811
  function writeArtifactsManifest(context, artifacts) {
21599
- writeFileSync10(join35(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
21812
+ writeFileSync10(join36(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
21600
21813
  `);
21601
21814
  }
21602
21815
  function collectRunArtifacts(context) {
@@ -21609,14 +21822,14 @@ function collectRunArtifacts(context) {
21609
21822
  artifacts.push({
21610
21823
  path: toProjectRelative(context.targetDir, path),
21611
21824
  mime: mimeForPath(path),
21612
- sha256: createHash23("sha256").update(bytes).digest("hex"),
21825
+ sha256: createHash24("sha256").update(bytes).digest("hex"),
21613
21826
  sizeBytes: stat2.size
21614
21827
  });
21615
21828
  }
21616
21829
  return artifacts.sort((a, b) => a.path.localeCompare(b.path));
21617
21830
  }
21618
21831
  function readRunRecord(runDir) {
21619
- const path = join35(runDir, "run.json");
21832
+ const path = join36(runDir, "run.json");
21620
21833
  if (!existsSync16(path))
21621
21834
  return null;
21622
21835
  try {
@@ -21628,7 +21841,7 @@ function readRunRecord(runDir) {
21628
21841
  function walkFiles(dir) {
21629
21842
  const files = [];
21630
21843
  for (const entry of readdirSync13(dir)) {
21631
- const full = join35(dir, entry);
21844
+ const full = join36(dir, entry);
21632
21845
  if (statSync12(full).isDirectory())
21633
21846
  files.push(...walkFiles(full));
21634
21847
  else
@@ -21737,7 +21950,7 @@ __export(exports_external, {
21737
21950
  pipe: () => pipe,
21738
21951
  partialRecord: () => partialRecord,
21739
21952
  parseAsync: () => parseAsync2,
21740
- parse: () => parse9,
21953
+ parse: () => parse10,
21741
21954
  overwrite: () => _overwrite,
21742
21955
  output: () => output,
21743
21956
  optional: () => optional,
@@ -21957,7 +22170,7 @@ __export(exports_core2, {
21957
22170
  prettifyError: () => prettifyError,
21958
22171
  parseURLObject: () => parseURLObject,
21959
22172
  parseAsync: () => parseAsync,
21960
- parse: () => parse7,
22173
+ parse: () => parse8,
21961
22174
  meta: () => meta,
21962
22175
  mergeValues: () => mergeValues,
21963
22176
  memoizer: () => memoizer,
@@ -23411,7 +23624,7 @@ var _parse = (_Err) => {
23411
23624
  };
23412
23625
  return fn;
23413
23626
  };
23414
- var parse7 = /* @__PURE__ */ _parse($ZodRealError);
23627
+ var parse8 = /* @__PURE__ */ _parse($ZodRealError);
23415
23628
  var _parseAsync = (_Err) => {
23416
23629
  const fn = async (schema, value, _ctx, params) => {
23417
23630
  const ctx = _ctx ? { ..._ctx, async: true } : { async: true };
@@ -23482,18 +23695,18 @@ var validateAsync = async (schema, value, _ctx) => {
23482
23695
  return result.issues.length === 0;
23483
23696
  };
23484
23697
  var _encode = (_Err) => {
23485
- const parse8 = _parse(_Err);
23698
+ const parse9 = _parse(_Err);
23486
23699
  const fn = (schema, value, _ctx, _params) => {
23487
23700
  const ctx = _ctx ? { ..._ctx, direction: "backward" } : { direction: "backward" };
23488
- return parse8(schema, value, ctx, finalizeParams(fn, _params));
23701
+ return parse9(schema, value, ctx, finalizeParams(fn, _params));
23489
23702
  };
23490
23703
  return fn;
23491
23704
  };
23492
23705
  var encode = /* @__PURE__ */ _encode($ZodRealError);
23493
23706
  var _decode = (_Err) => {
23494
- const parse8 = _parse(_Err);
23707
+ const parse9 = _parse(_Err);
23495
23708
  const fn = (schema, value, _ctx, _params) => {
23496
- return parse8(schema, value, _ctx, finalizeParams(fn, _params));
23709
+ return parse9(schema, value, _ctx, finalizeParams(fn, _params));
23497
23710
  };
23498
23711
  return fn;
23499
23712
  };
@@ -26416,10 +26629,10 @@ var $ZodFunction = /* @__PURE__ */ $constructor("$ZodFunction", (inst, def) => {
26416
26629
  throw new Error("implement() must be called with a function");
26417
26630
  }
26418
26631
  return Object.defineProperty(function(...args) {
26419
- const parsedArgs = inst._def.input ? parse7(inst._def.input, args) : args;
26632
+ const parsedArgs = inst._def.input ? parse8(inst._def.input, args) : args;
26420
26633
  const result = Reflect.apply(func, this, parsedArgs);
26421
26634
  if (inst._def.output) {
26422
- return parse7(inst._def.output, result);
26635
+ return parse8(inst._def.output, result);
26423
26636
  }
26424
26637
  return result;
26425
26638
  }, "_zod", { value: inst._zod, enumerable: false });
@@ -38057,7 +38270,7 @@ var ZodRealError = /* @__PURE__ */ $constructor("ZodError", initializer2, undefi
38057
38270
  });
38058
38271
 
38059
38272
  // node_modules/zod/v4/classic/parse.js
38060
- var parse9 = /* @__PURE__ */ _parse(ZodRealError);
38273
+ var parse10 = /* @__PURE__ */ _parse(ZodRealError);
38061
38274
  var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
38062
38275
  var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
38063
38276
  var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
@@ -38193,7 +38406,7 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
38193
38406
  exports_util.own(this, "~standard", value);
38194
38407
  },
38195
38408
  parse: function _parse2(data, params) {
38196
- return parse9(this, data, params, { callee: _parse2 });
38409
+ return parse10(this, data, params, { callee: _parse2 });
38197
38410
  },
38198
38411
  parseAsync: async function _parseAsync2(data, params) {
38199
38412
  return await parseAsync2(this, data, params, { callee: _parseAsync2 });
@@ -41028,7 +41241,7 @@ function clone2(value) {
41028
41241
  return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
41029
41242
  }
41030
41243
  // src/lib/canonical-json.ts
41031
- import { createHash as createHash24 } from "crypto";
41244
+ import { createHash as createHash25 } from "crypto";
41032
41245
  function canonicalJson2(value, maximumBytes = 2 * 1024 * 1024) {
41033
41246
  return canonicalJsonAtDepth(value, maximumBytes, 64);
41034
41247
  }
@@ -41072,7 +41285,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
41072
41285
  return visit3(value, 0);
41073
41286
  }
41074
41287
  function canonicalJsonSha256(value, maximumBytes) {
41075
- return createHash24("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
41288
+ return createHash25("sha256").update(canonicalJson2(value, maximumBytes)).digest("hex");
41076
41289
  }
41077
41290
 
41078
41291
  // src/lib/remote-recurring-contract.ts
@@ -43136,9 +43349,9 @@ async function createRemoteSkillsClient(env = process.env) {
43136
43349
  }
43137
43350
  // src/lib/scheduler.ts
43138
43351
  import { existsSync as existsSync17, readFileSync as readFileSync23, writeFileSync as writeFileSync11, mkdirSync as mkdirSync12 } from "fs";
43139
- import { join as join36 } from "path";
43352
+ import { join as join37 } from "path";
43140
43353
  function getSchedulesPath(targetDir = process.cwd()) {
43141
- return join36(targetDir, ".skills", "schedules.json");
43354
+ return join37(targetDir, ".skills", "schedules.json");
43142
43355
  }
43143
43356
  function loadSchedules(targetDir = process.cwd()) {
43144
43357
  const path = getSchedulesPath(targetDir);
@@ -43151,7 +43364,7 @@ function loadSchedules(targetDir = process.cwd()) {
43151
43364
  }
43152
43365
  function saveSchedules(data, targetDir = process.cwd()) {
43153
43366
  const path = getSchedulesPath(targetDir);
43154
- const dir = join36(targetDir, ".skills");
43367
+ const dir = join37(targetDir, ".skills");
43155
43368
  if (!existsSync17(dir))
43156
43369
  mkdirSync12(dir, { recursive: true });
43157
43370
  writeFileSync11(path, JSON.stringify(data, null, 2));
@@ -43339,7 +43552,7 @@ function recordScheduleRun(id, status, targetDir) {
43339
43552
  saveSchedules(data, targetDir);
43340
43553
  }
43341
43554
  // src/lib/revision.ts
43342
- import { createHash as createHash25 } from "crypto";
43555
+ import { createHash as createHash26 } from "crypto";
43343
43556
  var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
43344
43557
  function revisionIdOf(content) {
43345
43558
  const canonicalContent = {
@@ -43360,15 +43573,15 @@ function revisionIdOf(content) {
43360
43573
  canonicalContent.archiveReason = content.archiveReason ?? null;
43361
43574
  canonicalContent.replacementSlug = content.replacementSlug ?? null;
43362
43575
  }
43363
- const canonical6 = JSON.stringify(canonicalContent);
43364
- return createHash25("sha256").update(canonical6).digest("hex");
43576
+ const canonical7 = JSON.stringify(canonicalContent);
43577
+ return createHash26("sha256").update(canonical7).digest("hex");
43365
43578
  }
43366
43579
  function revisionIdOfRecord(record8) {
43367
43580
  return revisionIdOf(record8);
43368
43581
  }
43369
43582
  // src/lib/pull.ts
43370
43583
  import { existsSync as existsSync18, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync3, readFileSync as readFileSync25, renameSync as renameSync7, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "fs";
43371
- import { dirname as dirname22, join as join37 } from "path";
43584
+ import { dirname as dirname22, join as join38 } from "path";
43372
43585
 
43373
43586
  // src/lib/skill-bundles.ts
43374
43587
  import { createHmac, timingSafeEqual } from "crypto";
@@ -43462,7 +43675,7 @@ async function pullOne(client2, rawName, corpusOptions, verify) {
43462
43675
  return reconcileTombstone(slug2, corpusOptions);
43463
43676
  }
43464
43677
  if (bundleResponse.status === 404) {
43465
- const marker = readPullMarker(join37(getPortableSkillsRoot(corpusOptions), slug2));
43678
+ const marker = readPullMarker(join38(getPortableSkillsRoot(corpusOptions), slug2));
43466
43679
  if (isPublishedInstallMarker(marker)) {
43467
43680
  return { name: slug2, success: true, purged: true, removed: false };
43468
43681
  }
@@ -43501,7 +43714,7 @@ async function pullOne(client2, rawName, corpusOptions, verify) {
43501
43714
  return { name: slug2, success: false, error: `Skill '${slug2}' was not found on the configured Skills instance.` };
43502
43715
  }
43503
43716
  if (!meta3?.revisionId) {
43504
- const marker = readPullMarker(join37(getPortableSkillsRoot(corpusOptions), slug2));
43717
+ const marker = readPullMarker(join38(getPortableSkillsRoot(corpusOptions), slug2));
43505
43718
  if (isPublishedInstallMarker(marker)) {
43506
43719
  return { name: slug2, success: true, purged: true, removed: false };
43507
43720
  }
@@ -43558,7 +43771,7 @@ function provenRevision(meta3, slug2, bundle2) {
43558
43771
  return declared;
43559
43772
  }
43560
43773
  function reconcileTombstone(slug2, corpusOptions) {
43561
- const target = join37(getPortableSkillsRoot(corpusOptions), slug2);
43774
+ const target = join38(getPortableSkillsRoot(corpusOptions), slug2);
43562
43775
  if (!hasSkillsOwnershipMarker(target)) {
43563
43776
  return { name: slug2, success: true, tombstoned: true, removed: false, leftInPlace: true };
43564
43777
  }
@@ -43567,7 +43780,7 @@ function reconcileTombstone(slug2, corpusOptions) {
43567
43780
  }
43568
43781
  function readPullMarker(dir) {
43569
43782
  try {
43570
- return JSON.parse(readFileSync25(join37(dir, PULL_MARKER_FILE), "utf-8"));
43783
+ return JSON.parse(readFileSync25(join38(dir, PULL_MARKER_FILE), "utf-8"));
43571
43784
  } catch {
43572
43785
  return null;
43573
43786
  }
@@ -43702,14 +43915,14 @@ function verifyBundleResponseBytes(buffer, response, verify = {}) {
43702
43915
  function installBundleAtomically(name, entries, options = {}, marker = {}) {
43703
43916
  const root2 = getPortableSkillsRoot(options);
43704
43917
  mkdirSync13(root2, { recursive: true });
43705
- const target = join37(root2, name);
43918
+ const target = join38(root2, name);
43706
43919
  const created = !existsSync18(target);
43707
- const staging = mkdtempSync3(join37(root2, `.pull-${name}-`));
43920
+ const staging = mkdtempSync3(join38(root2, `.pull-${name}-`));
43708
43921
  let moved = false;
43709
43922
  let backup = null;
43710
43923
  try {
43711
43924
  for (const entry of entries) {
43712
- const destination = join37(staging, entry.path);
43925
+ const destination = join38(staging, entry.path);
43713
43926
  mkdirSync13(dirname22(destination), { recursive: true });
43714
43927
  writeFileSync12(destination, entry.bytes, { mode: entry.mode });
43715
43928
  }
@@ -43722,8 +43935,8 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
43722
43935
  ...marker.revisionId ? { revisionId: marker.revisionId } : {}
43723
43936
  });
43724
43937
  if (existsSync18(target)) {
43725
- backup = mkdtempSync3(join37(root2, `.pull-backup-${name}-`));
43726
- renameSync7(target, join37(backup, name));
43938
+ backup = mkdtempSync3(join38(root2, `.pull-backup-${name}-`));
43939
+ renameSync7(target, join38(backup, name));
43727
43940
  moved = true;
43728
43941
  }
43729
43942
  renameSync7(staging, target);
@@ -43731,9 +43944,9 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
43731
43944
  rmSync5(backup, { recursive: true, force: true });
43732
43945
  } catch (error61) {
43733
43946
  rmSync5(staging, { recursive: true, force: true });
43734
- if (moved && backup && existsSync18(join37(backup, name))) {
43947
+ if (moved && backup && existsSync18(join38(backup, name))) {
43735
43948
  try {
43736
- renameSync7(join37(backup, name), target);
43949
+ renameSync7(join38(backup, name), target);
43737
43950
  } catch {}
43738
43951
  }
43739
43952
  throw error61;
@@ -43752,7 +43965,7 @@ function writePullMarker(dir, record8) {
43752
43965
  ...record8.revisionId ? { revisionId: record8.revisionId } : {},
43753
43966
  syncedAt: new Date().toISOString()
43754
43967
  };
43755
- writeFileSync12(join37(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
43968
+ writeFileSync12(join38(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
43756
43969
  `);
43757
43970
  }
43758
43971
  async function safeMeta(client2, slug2) {
@@ -43902,7 +44115,7 @@ async function requestInvitationEmail(origin, action, input2) {
43902
44115
  }
43903
44116
 
43904
44117
  // src/lib/remote-private-publications.ts
43905
- import { createHash as createHash26 } from "crypto";
44118
+ import { createHash as createHash27 } from "crypto";
43906
44119
  var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
43907
44120
  var failures2 = {
43908
44121
  INVALID_REQUEST: [400, "The publication request is invalid."],
@@ -43949,7 +44162,7 @@ var hash3 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
43949
44162
  var record9 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
43950
44163
  var exact = (v, keys) => record9(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
43951
44164
  var date5 = (v) => typeof v === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v) && Number.isFinite(Date.parse(v));
43952
- var publicationSha256 = (bytes) => createHash26("sha256").update(bytes).digest("hex");
44165
+ var publicationSha256 = (bytes) => createHash27("sha256").update(bytes).digest("hex");
43953
44166
  function checkedPublicationDeclaration(value) {
43954
44167
  if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash3(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
43955
44168
  return bad();
@@ -45756,10 +45969,10 @@ function clone3(value) {
45756
45969
  }
45757
45970
  // src/lib/feedback.ts
45758
45971
  import { appendFileSync, existsSync as existsSync19, mkdirSync as mkdirSync15 } from "fs";
45759
- import { dirname as dirname24, join as join38 } from "path";
45972
+ import { dirname as dirname24, join as join39 } from "path";
45760
45973
  import { Database } from "bun:sqlite";
45761
45974
  function getFeedbackDbPath() {
45762
- return join38(getDataDir(), "skills.db");
45975
+ return join39(getDataDir(), "skills.db");
45763
45976
  }
45764
45977
  function getFeedbackDb() {
45765
45978
  const dbPath = getFeedbackDbPath();
@@ -45791,7 +46004,7 @@ function saveFeedback(input2) {
45791
46004
  throw new Error("Feedback message is required");
45792
46005
  const category = input2.category ?? "general";
45793
46006
  if (isApiMode()) {
45794
- const path = join38(getDataDir(), "feedback.jsonl");
46007
+ const path = join39(getDataDir(), "feedback.jsonl");
45795
46008
  const dir = dirname24(path);
45796
46009
  if (!existsSync19(dir))
45797
46010
  mkdirSync15(dir, { recursive: true });
@@ -45815,7 +46028,7 @@ function isApiMode(env = process.env) {
45815
46028
  }
45816
46029
  }
45817
46030
  // src/lib/native-storage.ts
45818
- import { createHash as createHash27, createHmac as createHmac2 } from "crypto";
46031
+ import { createHash as createHash28, createHmac as createHmac2 } from "crypto";
45819
46032
  import {
45820
46033
  existsSync as existsSync20,
45821
46034
  mkdirSync as mkdirSync16,
@@ -45824,7 +46037,7 @@ import {
45824
46037
  statSync as statSync13,
45825
46038
  writeFileSync as writeFileSync14
45826
46039
  } from "fs";
45827
- import { dirname as dirname25, join as join39, normalize as normalize5, relative as relative10, sep as sep10 } from "path";
46040
+ import { dirname as dirname25, join as join40, normalize as normalize5, relative as relative10, sep as sep11 } from "path";
45828
46041
  var SKILLS_STORAGE_TABLES = [
45829
46042
  "skills_sync_records",
45830
46043
  "skills_sync_cursors"
@@ -45961,7 +46174,7 @@ function getSkillsNativeStorageStatus(options = {}) {
45961
46174
  local: {
45962
46175
  dataDir: getDataDir(),
45963
46176
  projectStateDir: getProjectStateDir(targetDir),
45964
- feedbackDbPath: join39(getDataDir(), "skills.db")
46177
+ feedbackDbPath: join40(getDataDir(), "skills.db")
45965
46178
  },
45966
46179
  remote: {
45967
46180
  databaseConfigured: Boolean(config2.databaseUrl),
@@ -45991,7 +46204,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
45991
46204
  files.push({
45992
46205
  path: relativePath,
45993
46206
  sizeBytes: bytes.byteLength,
45994
- sha256: createHash27("sha256").update(bytes).digest("hex"),
46207
+ sha256: createHash28("sha256").update(bytes).digest("hex"),
45995
46208
  ...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
45996
46209
  });
45997
46210
  }
@@ -46016,7 +46229,7 @@ function importSkillsLocalSnapshot(snapshot2, targetDir = process.cwd(), options
46016
46229
  continue;
46017
46230
  }
46018
46231
  const bytes = Buffer.from(file2.contentBase64, "base64");
46019
- const hash4 = createHash27("sha256").update(bytes).digest("hex");
46232
+ const hash4 = createHash28("sha256").update(bytes).digest("hex");
46020
46233
  if (hash4 !== file2.sha256) {
46021
46234
  throw new Error(`Snapshot file checksum mismatch: ${file2.path}`);
46022
46235
  }
@@ -46318,7 +46531,7 @@ function parsePositiveInteger(value) {
46318
46531
  function walkFiles2(dir) {
46319
46532
  const files = [];
46320
46533
  for (const entry of readdirSync14(dir)) {
46321
- const full = join39(dir, entry);
46534
+ const full = join40(dir, entry);
46322
46535
  const stats = statSync13(full);
46323
46536
  if (stats.isDirectory())
46324
46537
  files.push(...walkFiles2(full));
@@ -46329,13 +46542,13 @@ function walkFiles2(dir) {
46329
46542
  }
46330
46543
  function resolveSnapshotPath(targetDir, snapshotPath) {
46331
46544
  const normalizedPath = normalize5(snapshotPath);
46332
- if (normalizedPath.startsWith("..") || normalizedPath.includes(`${sep10}..${sep10}`) || normalizedPath.startsWith(sep10)) {
46545
+ if (normalizedPath.startsWith("..") || normalizedPath.includes(`${sep11}..${sep11}`) || normalizedPath.startsWith(sep11)) {
46333
46546
  throw new Error(`Unsafe snapshot path: ${snapshotPath}`);
46334
46547
  }
46335
46548
  if (!toPosix(normalizedPath).startsWith(".skills/")) {
46336
46549
  throw new Error(`Snapshot path must stay inside .skills: ${snapshotPath}`);
46337
46550
  }
46338
- return join39(targetDir, normalizedPath);
46551
+ return join40(targetDir, normalizedPath);
46339
46552
  }
46340
46553
  function normalizeS3Prefix(prefix) {
46341
46554
  return (prefix ?? "").trim().replace(/^\/+|\/+$/g, "");
@@ -46348,7 +46561,7 @@ function toIsoString(value) {
46348
46561
  return Number.isNaN(date6.getTime()) ? value : date6.toISOString();
46349
46562
  }
46350
46563
  function sha256Hex2(value) {
46351
- return createHash27("sha256").update(value).digest("hex");
46564
+ return createHash28("sha256").update(value).digest("hex");
46352
46565
  }
46353
46566
  function normalizeHeaders(headers) {
46354
46567
  const result = {};
@@ -46400,7 +46613,7 @@ function toArrayBuffer(bytes) {
46400
46613
  return buffer;
46401
46614
  }
46402
46615
  // src/lib/station-snapshot.ts
46403
- import { createHash as createHash28 } from "crypto";
46616
+ import { createHash as createHash29 } from "crypto";
46404
46617
  import {
46405
46618
  copyFileSync,
46406
46619
  mkdirSync as mkdirSync17,
@@ -46408,12 +46621,12 @@ import {
46408
46621
  statSync as statSync15,
46409
46622
  writeFileSync as writeFileSync15
46410
46623
  } from "fs";
46411
- import { dirname as dirname26, isAbsolute as isAbsolute21, relative as relative11, resolve as resolve21, sep as sep12 } from "path";
46624
+ import { dirname as dirname26, isAbsolute as isAbsolute21, relative as relative11, resolve as resolve21, sep as sep13 } from "path";
46412
46625
 
46413
46626
  // src/lib/portable-snapshot-filter.ts
46414
46627
  import { readdirSync as readdirSync15, statSync as statSync14 } from "fs";
46415
46628
  import { homedir as homedir7 } from "os";
46416
- import { join as join40, sep as sep11 } from "path";
46629
+ import { join as join41, sep as sep12 } from "path";
46417
46630
  var SYNC_HOMES = [
46418
46631
  { name: "skills", subClass: "skills", agent: null },
46419
46632
  { name: "custom", subClass: "custom", agent: null },
@@ -46506,16 +46719,16 @@ function isPortableWithinSkill(relativeParts) {
46506
46719
  function homePathFor(definition, homesRoot) {
46507
46720
  const home = homesRoot ?? homedir7();
46508
46721
  if (definition.subClass === "skills" || definition.subClass === "custom") {
46509
- return join40(skillsDataRootForHome(home), definition.name);
46722
+ return join41(skillsDataRootForHome(home), definition.name);
46510
46723
  }
46511
46724
  if (definition.agent === "opencode") {
46512
- return join40(home, ".config", "opencode", "skills");
46725
+ return join41(home, ".config", "opencode", "skills");
46513
46726
  }
46514
- return join40(home, `.${definition.agent}`, "skills");
46727
+ return join41(home, `.${definition.agent}`, "skills");
46515
46728
  }
46516
46729
  function destinationFor(definition, stationId, relativePath) {
46517
- const category = definition.subClass === "agent-homes" ? join40("agent-homes", definition.agent ?? "") : definition.name;
46518
- return join40("resources", stationId, "skills", category, ...relativePath.split(sep11));
46730
+ const category = definition.subClass === "agent-homes" ? join41("agent-homes", definition.agent ?? "") : definition.name;
46731
+ return join41("resources", stationId, "skills", category, ...relativePath.split(sep12));
46519
46732
  }
46520
46733
  function walkEntries(absoluteRoot) {
46521
46734
  let entries;
@@ -46526,7 +46739,7 @@ function walkEntries(absoluteRoot) {
46526
46739
  }
46527
46740
  const output2 = [];
46528
46741
  for (const entry of entries) {
46529
- const childFull = join40(absoluteRoot, entry.name);
46742
+ const childFull = join41(absoluteRoot, entry.name);
46530
46743
  if (entry.isSymbolicLink()) {
46531
46744
  output2.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
46532
46745
  continue;
@@ -46537,7 +46750,7 @@ function walkEntries(absoluteRoot) {
46537
46750
  }
46538
46751
  const nested = walkEntries(childFull);
46539
46752
  for (const item of nested) {
46540
- output2.push({ ...item, relativePath: join40(entry.name, item.relativePath) });
46753
+ output2.push({ ...item, relativePath: join41(entry.name, item.relativePath) });
46541
46754
  }
46542
46755
  continue;
46543
46756
  }
@@ -46575,7 +46788,7 @@ function validateStationId(stationId) {
46575
46788
  }
46576
46789
  }
46577
46790
  function sha256File(filePath) {
46578
- return createHash28("sha256").update(readFileSync27(filePath)).digest("hex");
46791
+ return createHash29("sha256").update(readFileSync27(filePath)).digest("hex");
46579
46792
  }
46580
46793
  function scanHome(definition, homesRoot) {
46581
46794
  const homePath = homePathFor(definition, homesRoot);
@@ -46583,7 +46796,7 @@ function scanHome(definition, homesRoot) {
46583
46796
  const portable = [];
46584
46797
  const skipped = [];
46585
46798
  for (const entry of entries) {
46586
- const relativeParts = entry.relativePath.split(sep12);
46799
+ const relativeParts = entry.relativePath.split(sep13);
46587
46800
  const fileName = relativeParts[relativeParts.length - 1];
46588
46801
  if (entry.kind === "symlink") {
46589
46802
  skipped.push({ relativePath: entry.relativePath, reason: "symlink" });
@@ -46674,7 +46887,7 @@ function writeStationSnapshot(options) {
46674
46887
  for (const plan of plans) {
46675
46888
  const destination = resolve21(repoRoot, plan.destination);
46676
46889
  const destinationRelative = relative11(repoRoot, destination);
46677
- if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep12) || isAbsolute21(destinationRelative)) {
46890
+ if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep13) || isAbsolute21(destinationRelative)) {
46678
46891
  throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
46679
46892
  }
46680
46893
  let existingDigest = null;
@@ -46726,7 +46939,7 @@ function writeStationSnapshot(options) {
46726
46939
  };
46727
46940
  }
46728
46941
  // src/lib/station-hydrate.ts
46729
- import { createHash as createHash29 } from "crypto";
46942
+ import { createHash as createHash30 } from "crypto";
46730
46943
  import {
46731
46944
  copyFileSync as copyFileSync2,
46732
46945
  mkdirSync as mkdirSync18,
@@ -46735,7 +46948,7 @@ import {
46735
46948
  statSync as statSync16,
46736
46949
  writeFileSync as writeFileSync16
46737
46950
  } from "fs";
46738
- import { dirname as dirname27, join as join41, resolve as resolve22, sep as sep13 } from "path";
46951
+ import { dirname as dirname27, join as join42, resolve as resolve22, sep as sep14 } from "path";
46739
46952
  var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
46740
46953
  var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
46741
46954
  var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
@@ -46743,11 +46956,11 @@ function fail2(code2, message, detail = []) {
46743
46956
  throw new StationSnapshotError(code2, message, detail);
46744
46957
  }
46745
46958
  function snapshotRootFor(repoRoot, stationId) {
46746
- return join41(repoRoot, "resources", stationId, "skills");
46959
+ return join42(repoRoot, "resources", stationId, "skills");
46747
46960
  }
46748
46961
  function readSnapshotManifest(repoRoot, stationId) {
46749
46962
  const snapshotRoot = snapshotRootFor(repoRoot, stationId);
46750
- const manifestPath = join41(snapshotRoot, "sync-manifest.json");
46963
+ const manifestPath = join42(snapshotRoot, "sync-manifest.json");
46751
46964
  let manifest;
46752
46965
  try {
46753
46966
  manifest = JSON.parse(readFileSync28(manifestPath, "utf8"));
@@ -46774,7 +46987,7 @@ function planStationHydration(stationId, repoRoot) {
46774
46987
  const hashMismatches = [];
46775
46988
  const skippedByRule = [];
46776
46989
  for (const agent of SYNC_AGENTS) {
46777
- const agentRoot = join41(snapshotRoot, "agent-homes", agent);
46990
+ const agentRoot = join42(snapshotRoot, "agent-homes", agent);
46778
46991
  let identEntries;
46779
46992
  try {
46780
46993
  identEntries = readdirSync16(agentRoot, { withFileTypes: true });
@@ -46785,10 +46998,10 @@ function planStationHydration(stationId, repoRoot) {
46785
46998
  if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
46786
46999
  continue;
46787
47000
  }
46788
- const identRoot = join41(agentRoot, identEntry.name);
47001
+ const identRoot = join42(agentRoot, identEntry.name);
46789
47002
  const entries = walkEntries(identRoot);
46790
47003
  for (const entry of entries) {
46791
- const relativeParts = [identEntry.name, ...entry.relativePath.split(sep13)];
47004
+ const relativeParts = [identEntry.name, ...entry.relativePath.split(sep14)];
46792
47005
  if (entry.kind === "symlink") {
46793
47006
  symlinks.push({ ident: identEntry.name, agent, relativePath: entry.relativePath });
46794
47007
  continue;
@@ -46812,8 +47025,8 @@ function planStationHydration(stationId, repoRoot) {
46812
47025
  });
46813
47026
  continue;
46814
47027
  }
46815
- const withinIdent = relativeParts.slice(1).join(sep13);
46816
- const homeRelative = relativeParts.join(sep13);
47028
+ const withinIdent = relativeParts.slice(1).join(sep14);
47029
+ const homeRelative = relativeParts.join(sep14);
46817
47030
  if (REFUSED_SCANNER_FLAGGED.has(homeRelative)) {
46818
47031
  skippedByRule.push({
46819
47032
  ident: identEntry.name,
@@ -46932,7 +47145,7 @@ function skillSha256(skill) {
46932
47145
  return sha256File(skill.files[0].winner.fullPath);
46933
47146
  }
46934
47147
  const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
46935
- return createHash29("sha256").update(joined.sort().join(`
47148
+ return createHash30("sha256").update(joined.sort().join(`
46936
47149
  `)).digest("hex");
46937
47150
  }
46938
47151
  function writeStationHydration(options) {
@@ -46970,7 +47183,7 @@ function writeStationHydration(options) {
46970
47183
  const toWrite = [];
46971
47184
  for (const skill of plan.winners) {
46972
47185
  for (const file2 of skill.files) {
46973
- const destination = join41(cacheRoot, skill.ident, file2.withinIdent);
47186
+ const destination = join42(cacheRoot, skill.ident, file2.withinIdent);
46974
47187
  const digest3 = sha256File(file2.winner.fullPath);
46975
47188
  let existingDigest = null;
46976
47189
  try {
@@ -47012,7 +47225,7 @@ function writeStationHydration(options) {
47012
47225
  },
47013
47226
  skills: resultSkills
47014
47227
  };
47015
- const hydrationManifestPath = join41(dirname27(cacheRoot), `hydration-${options.stationId}.json`);
47228
+ const hydrationManifestPath = join42(dirname27(cacheRoot), `hydration-${options.stationId}.json`);
47016
47229
  mkdirSync18(dirname27(hydrationManifestPath), { recursive: true });
47017
47230
  writeFileSync16(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
47018
47231
  `);
@@ -47025,7 +47238,7 @@ function writeStationHydration(options) {
47025
47238
  }
47026
47239
  // src/lib/private-publication-recovery.ts
47027
47240
  import { constants as constants14, closeSync as closeSync14, fsyncSync as fsyncSync6, fstatSync as fstatSync14, lstatSync as lstatSync26, mkdirSync as mkdirSync19, openSync as openSync14, readSync as readSync12, realpathSync as realpathSync13, renameSync as renameSync8, unlinkSync as unlinkSync6, writeFileSync as writeFileSync17 } from "fs";
47028
- import { dirname as dirname28, isAbsolute as isAbsolute22, join as join42, resolve as resolve23 } from "path";
47241
+ import { dirname as dirname28, isAbsolute as isAbsolute22, join as join43, resolve as resolve23 } from "path";
47029
47242
  import { randomUUID as randomUUID5 } from "crypto";
47030
47243
  var fail3 = () => {
47031
47244
  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.");
@@ -47051,7 +47264,7 @@ function unchangedDirectory(directory2, identity5) {
47051
47264
  return fail3();
47052
47265
  }
47053
47266
  function readOwned(directory2, name, max) {
47054
- const identity5 = safeDirectory(directory2), file2 = join42(directory2, name);
47267
+ const identity5 = safeDirectory(directory2), file2 = join43(directory2, name);
47055
47268
  const fd = openSync14(file2, constants14.O_RDONLY | constants14.O_NOFOLLOW);
47056
47269
  try {
47057
47270
  const stat2 = fstatSync14(fd);
@@ -47075,7 +47288,7 @@ function readOwned(directory2, name, max) {
47075
47288
  }
47076
47289
  }
47077
47290
  function writeOwned(directory2, name, bytes) {
47078
- const fd = openSync14(join42(directory2, name), constants14.O_WRONLY | constants14.O_CREAT | constants14.O_EXCL | constants14.O_NOFOLLOW, 384);
47291
+ const fd = openSync14(join43(directory2, name), constants14.O_WRONLY | constants14.O_CREAT | constants14.O_EXCL | constants14.O_NOFOLLOW, 384);
47079
47292
  try {
47080
47293
  writeFileSync17(fd, bytes);
47081
47294
  fsyncSync6(fd);
@@ -47088,7 +47301,7 @@ function save(directory2, value) {
47088
47301
  writeOwned(directory2, temporary, JSON.stringify(value) + `
47089
47302
  `);
47090
47303
  unchangedDirectory(directory2, identity5);
47091
- renameSync8(join42(directory2, temporary), join42(directory2, "receipt.json"));
47304
+ renameSync8(join43(directory2, temporary), join43(directory2, "receipt.json"));
47092
47305
  const fd = openSync14(directory2, constants14.O_RDONLY | constants14.O_NOFOLLOW);
47093
47306
  try {
47094
47307
  fsyncSync6(fd);
@@ -47101,7 +47314,7 @@ function bind(client2, receipt) {
47101
47314
  throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
47102
47315
  }
47103
47316
  async function locked(directory2, action) {
47104
- const identity5 = safeDirectory(directory2), file2 = join42(directory2, "operation.lock");
47317
+ const identity5 = safeDirectory(directory2), file2 = join43(directory2, "operation.lock");
47105
47318
  let fd;
47106
47319
  try {
47107
47320
  fd = openSync14(file2, constants14.O_WRONLY | constants14.O_CREAT | constants14.O_EXCL | constants14.O_NOFOLLOW, 384);
@@ -47260,7 +47473,7 @@ async function inspectLocked(client2, directory2, cancel) {
47260
47473
  return privatePublicationResult(directory2, receipt);
47261
47474
  }
47262
47475
  // src/lib/skill-context.ts
47263
- import { createHash as createHash30 } from "crypto";
47476
+ import { createHash as createHash31 } from "crypto";
47264
47477
  import { resolve as resolve24 } from "path";
47265
47478
  function words(value) {
47266
47479
  return new Set(value.toLowerCase().match(/[a-z0-9]+/g) ?? []);
@@ -47353,7 +47566,7 @@ ${content}`;
47353
47566
  `);
47354
47567
  const receipt = {
47355
47568
  schemaVersion: 1,
47356
- id: createHash30("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
47569
+ id: createHash31("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
47357
47570
  profileId: profile.profileId,
47358
47571
  profileRevision: profile.profileRevision,
47359
47572
  authority: profile.authority,
@@ -47369,9 +47582,9 @@ ${content}`;
47369
47582
  return { context: context2, selections, omitted, receipt };
47370
47583
  }
47371
47584
  // src/lib/session-reconciliation.ts
47372
- import { createHash as createHash31 } from "crypto";
47585
+ import { createHash as createHash32 } from "crypto";
47373
47586
  var SESSION_RECONCILIATION_PLAN_TTL_MS = 5 * 60 * 1000;
47374
- var digest3 = (value) => createHash31("sha256").update(JSON.stringify(value)).digest("hex");
47587
+ var digest3 = (value) => createHash32("sha256").update(JSON.stringify(value)).digest("hex");
47375
47588
  function exactTimestamp(value) {
47376
47589
  const parsed = value === undefined ? Number.NaN : Date.parse(value);
47377
47590
  if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {