@hasna/skills 0.7.1 → 0.7.2

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
@@ -10421,7 +10421,7 @@ function getAllTags() {
10421
10421
  }
10422
10422
  // src/lib/installer.ts
10423
10423
  import { existsSync as existsSync13, readFileSync as readFileSync14, rmSync as rmSync3 } from "fs";
10424
- import { dirname as dirname9, join as join18 } from "path";
10424
+ import { dirname as dirname11, join as join19 } from "path";
10425
10425
  import { homedir as homedir4 } from "os";
10426
10426
  import { fileURLToPath } from "url";
10427
10427
 
@@ -10429,7 +10429,7 @@ import { fileURLToPath } from "url";
10429
10429
  import {
10430
10430
  cpSync as cpSync3,
10431
10431
  existsSync as existsSync11,
10432
- lstatSync as lstatSync9,
10432
+ lstatSync as lstatSync11,
10433
10433
  mkdirSync as mkdirSync6,
10434
10434
  mkdtempSync as mkdtempSync2,
10435
10435
  readFileSync as readFileSync12,
@@ -10440,12 +10440,12 @@ import {
10440
10440
  writeFileSync as writeFileSync6
10441
10441
  } from "fs";
10442
10442
  import { homedir as homedir3 } from "os";
10443
- import { basename as basename3, dirname as dirname8, join as join16 } from "path";
10443
+ import { basename as basename3, dirname as dirname10, join as join17 } from "path";
10444
10444
 
10445
10445
  // src/lib/agent-integration.ts
10446
- import { createHash as createHash6, randomUUID as randomUUID2 } from "crypto";
10447
- import { existsSync as existsSync10, lstatSync as lstatSync8, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync11, readdirSync as readdirSync10, readlinkSync, realpathSync as realpathSync4, renameSync as renameSync3, rmdirSync, writeFileSync as writeFileSync5, unlinkSync as unlinkSync2, chmodSync, openSync as openSync3, closeSync as closeSync3, fsyncSync, fstatSync as fstatSync3, readSync as readSync3, constants as constants3, linkSync } from "fs";
10448
- import { dirname as dirname7, join as join15, resolve as resolve6, relative as relative4, isAbsolute as isAbsolute5, sep as sep4 } from "path";
10446
+ import { createHash as createHash8, randomUUID as randomUUID2 } from "crypto";
10447
+ import { existsSync as existsSync10, lstatSync as lstatSync10, statSync as statSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync11, readdirSync as readdirSync10, opendirSync as opendirSync3, readlinkSync, realpathSync as realpathSync4, renameSync as renameSync3, rmdirSync, writeFileSync as writeFileSync5, unlinkSync as unlinkSync2, chmodSync, openSync as openSync4, closeSync as closeSync4, fsyncSync, fstatSync as fstatSync4, readSync as readSync4, constants as constants4, linkSync } from "fs";
10448
+ import { dirname as dirname9, join as join16, resolve as resolve8, relative as relative4, isAbsolute as isAbsolute7, sep as sep4 } from "path";
10449
10449
  import { homedir as homedir2 } from "os";
10450
10450
 
10451
10451
  // src/lib/managed-policy.ts
@@ -11336,7 +11336,7 @@ function readSkillSession(sessionId, options = {}) {
11336
11336
  }
11337
11337
 
11338
11338
  // src/lib/agent-policy-limits.ts
11339
- var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRoots: 512, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
11339
+ var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
11340
11340
  function object(value) {
11341
11341
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
11342
11342
  }
@@ -11400,12 +11400,20 @@ function assertAgentPolicyCollections(policy) {
11400
11400
  requireBound(object(source));
11401
11401
  text(source.path);
11402
11402
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
11403
+ if (source.hashMode !== undefined)
11404
+ requireBound(source.hashMode === "bytes" && source.format === undefined && source.fields === undefined);
11403
11405
  if (source.format !== undefined)
11404
11406
  requireBound(["json", "toml", "yaml"].includes(source.format));
11405
11407
  if (source.fields !== undefined)
11406
11408
  for (const field of array(source.fields, AGENT_POLICY_LIMITS.fields))
11407
11409
  text(field, 256);
11408
11410
  }
11411
+ if (value.directories !== undefined)
11412
+ for (const directory of array(value.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
11413
+ requireBound(object(directory));
11414
+ text(directory.path);
11415
+ requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
11416
+ }
11409
11417
  if (value.builtinNames !== undefined)
11410
11418
  for (const name of array(value.builtinNames, AGENT_POLICY_LIMITS.builtinNames))
11411
11419
  text(name, 128);
@@ -11534,9 +11542,9 @@ function isOwnedCliBridge(path, expectedPaths) {
11534
11542
  }
11535
11543
 
11536
11544
  // src/lib/agent-discovery.ts
11537
- import { createHash as createHash5 } from "crypto";
11538
- import { lstatSync as lstatSync7, readFileSync as readFileSync10, readdirSync as readdirSync9, realpathSync as realpathSync3 } from "fs";
11539
- import { dirname as dirname6, isAbsolute as isAbsolute4, join as join14, resolve as resolve5, sep as sep3 } from "path";
11545
+ import { createHash as createHash7 } from "crypto";
11546
+ import { lstatSync as lstatSync9, readFileSync as readFileSync10, readdirSync as readdirSync9, realpathSync as realpathSync3 } from "fs";
11547
+ import { dirname as dirname8, isAbsolute as isAbsolute6, join as join15, resolve as resolve7, sep as sep3 } from "path";
11540
11548
 
11541
11549
  // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/index.js
11542
11550
  var composer = require_composer();
@@ -11654,6 +11662,10 @@ function configureHermesHooks(text2, supervisor, previous) {
11654
11662
  function assertHermesEnvironment(home) {
11655
11663
  if (process.env.TERMINAL_CWD)
11656
11664
  throw new Error("NATIVE_SKILL_DRIFT: Hermes TERMINAL_CWD requires a separately reviewed effective project; unset it before using this adapter");
11665
+ for (const variable of ["HERMES_BUNDLED_PLUGINS", "HERMES_BUNDLED_SKILLS"]) {
11666
+ if (process.env[variable])
11667
+ throw new Error(`NATIVE_SKILL_DRIFT: Hermes ${variable} requires a dedicated discovery adapter; unset it before using this adapter`);
11668
+ }
11657
11669
  const selected = process.env.HERMES_HOME?.trim();
11658
11670
  if (selected && (!isAbsolute3(selected) || resolve4(selected) !== join13(resolve4(home), ".hermes")))
11659
11671
  throw new Error("NATIVE_SKILL_DRIFT: custom Hermes homes/profiles require their own reviewed bridge");
@@ -11745,8 +11757,147 @@ if (failed) process.exitCode = 2;
11745
11757
  `;
11746
11758
  }
11747
11759
 
11760
+ // src/lib/agent-discovery-directories.ts
11761
+ import { createHash as createHash5 } from "crypto";
11762
+ import { lstatSync as lstatSync7, opendirSync as opendirSync2 } from "fs";
11763
+ import { dirname as dirname6, isAbsolute as isAbsolute4, join as join14, resolve as resolve5 } from "path";
11764
+ function need(value, message) {
11765
+ if (!value)
11766
+ throw new Error(message);
11767
+ }
11768
+ function safe(path) {
11769
+ need(typeof path === "string" && path.length <= AGENT_POLICY_LIMITS.pathCharacters && isAbsolute4(path) && resolve5(path) === path && !path.includes("\x00"), "Expected a canonical absolute discovery directory path");
11770
+ for (let cursor = path;; cursor = dirname6(cursor)) {
11771
+ need(!lstatSync7(cursor, { throwIfNoEntry: false })?.isSymbolicLink(), "Refusing symlink native discovery directory");
11772
+ if (dirname6(cursor) === cursor)
11773
+ break;
11774
+ }
11775
+ }
11776
+ function same(before, after) {
11777
+ return Boolean(after && after.isDirectory() && before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs);
11778
+ }
11779
+ function capture(path, budget) {
11780
+ safe(path);
11781
+ const root = lstatSync7(path, { throwIfNoEntry: false });
11782
+ if (!root)
11783
+ return { path, sha256: null };
11784
+ need(root.isDirectory(), "Unsupported native discovery directory input");
11785
+ const hash2 = createHash5("sha256");
11786
+ function walk2(current, relative4, before, depth) {
11787
+ need(depth <= 64, "Native discovery directory depth limit exceeded");
11788
+ const names = [], directory = opendirSync2(current);
11789
+ try {
11790
+ for (let entry = directory.readSync();entry; entry = directory.readSync()) {
11791
+ need(++budget.entries <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Native discovery directory entry limit exceeded");
11792
+ names.push(entry.name);
11793
+ }
11794
+ } finally {
11795
+ directory.closeSync();
11796
+ }
11797
+ need(new Set(names).size === names.length, "Ambiguous native discovery directory names");
11798
+ for (const name of names.sort()) {
11799
+ const child = join14(current, name), childRelative = relative4 ? `${relative4}/${name}` : name;
11800
+ need(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
11801
+ const stat = lstatSync7(child);
11802
+ need(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
11803
+ const row = JSON.stringify([childRelative, stat.isDirectory() ? "directory" : "file"]) + `
11804
+ `;
11805
+ budget.bytes += Buffer.byteLength(row);
11806
+ need(budget.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
11807
+ hash2.update(row);
11808
+ if (stat.isDirectory())
11809
+ walk2(child, childRelative, stat, depth + 1);
11810
+ }
11811
+ need(same(before, lstatSync7(current, { throwIfNoEntry: false })), "Native discovery directory changed while reading");
11812
+ }
11813
+ walk2(path, "", root, 0);
11814
+ safe(path);
11815
+ return { path, sha256: hash2.digest("hex") };
11816
+ }
11817
+ function captureDiscoveryDirectories(paths) {
11818
+ need(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
11819
+ const budget = { entries: 0, bytes: 0 };
11820
+ return paths.map((path) => capture(path, budget));
11821
+ }
11822
+ function verifyDiscoveryDirectories(directories) {
11823
+ need(Array.isArray(directories) && directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid native discovery directory collection");
11824
+ for (const directory of directories)
11825
+ need(directory && typeof directory === "object" && (directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256)), "Invalid native discovery directory digest");
11826
+ const current = captureDiscoveryDirectories(directories.map((directory) => directory.path));
11827
+ for (let i = 0;i < directories.length; i++)
11828
+ need(current[i].sha256 === directories[i].sha256, `Native discovery directory membership changed; run skills hook install with a fresh discovery review: ${directories[i].path}`);
11829
+ }
11830
+
11831
+ // src/lib/agent-discovery-bytes.ts
11832
+ import { createHash as createHash6 } from "crypto";
11833
+ import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync8, openSync as openSync3, readSync as readSync3 } from "fs";
11834
+ import { dirname as dirname7, isAbsolute as isAbsolute5, resolve as resolve6 } from "path";
11835
+ function discoveryByteBudget() {
11836
+ return { remaining: AGENT_POLICY_LIMITS.discoveryRawTotalBytes };
11837
+ }
11838
+ function safe2(path) {
11839
+ if (typeof path !== "string" || !isAbsolute5(path) || path !== resolve6(path) || path.length > AGENT_POLICY_LIMITS.pathCharacters || path.includes("\x00"))
11840
+ throw new Error("Expected a canonical absolute raw discovery path");
11841
+ for (let cursor = path;; cursor = dirname7(cursor)) {
11842
+ if (lstatSync8(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
11843
+ throw new Error("Refusing symlink raw discovery input");
11844
+ if (dirname7(cursor) === cursor)
11845
+ break;
11846
+ }
11847
+ }
11848
+ function same2(left, right) {
11849
+ return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
11850
+ }
11851
+ function consume(budget, bytes) {
11852
+ if (bytes > budget.remaining)
11853
+ throw new Error("Raw discovery aggregate byte limit exceeded");
11854
+ budget.remaining -= bytes;
11855
+ }
11856
+ function hashRawDiscoveryFile(path, budget, changes) {
11857
+ safe2(path);
11858
+ if (changes?.has(path)) {
11859
+ const text2 = changes.get(path), bytes = Buffer.byteLength(text2);
11860
+ if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
11861
+ throw new Error("Raw discovery file byte limit exceeded");
11862
+ consume(budget, bytes);
11863
+ return createHash6("sha256").update(text2).digest("hex");
11864
+ }
11865
+ const initial = lstatSync8(path, { throwIfNoEntry: false });
11866
+ if (!initial)
11867
+ return null;
11868
+ if (!initial.isFile() || initial.size > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
11869
+ throw new Error("Unsupported or oversized raw discovery input");
11870
+ if (initial.size > budget.remaining)
11871
+ throw new Error("Raw discovery aggregate byte limit exceeded");
11872
+ const fd = openSync3(path, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
11873
+ try {
11874
+ const opened = fstatSync3(fd);
11875
+ if (!opened.isFile() || !same2(initial, opened))
11876
+ throw new Error("Raw discovery input changed during open");
11877
+ const hash2 = createHash6("sha256"), buffer = Buffer.alloc(64 * 1024);
11878
+ let bytes = 0;
11879
+ while (true) {
11880
+ const count = readSync3(fd, buffer, 0, Math.min(buffer.length, AGENT_POLICY_LIMITS.discoveryRawSourceBytes - bytes + 1), null);
11881
+ if (!count)
11882
+ break;
11883
+ bytes += count;
11884
+ if (bytes > AGENT_POLICY_LIMITS.discoveryRawSourceBytes)
11885
+ throw new Error("Raw discovery file byte limit exceeded");
11886
+ consume(budget, count);
11887
+ hash2.update(buffer.subarray(0, count));
11888
+ }
11889
+ const final = fstatSync3(fd), current = lstatSync8(path, { throwIfNoEntry: false });
11890
+ safe2(path);
11891
+ if (!current?.isFile() || !same2(opened, final) || !same2(final, current) || bytes !== final.size)
11892
+ throw new Error("Raw discovery input changed during read");
11893
+ return hash2.digest("hex");
11894
+ } finally {
11895
+ closeSync3(fd);
11896
+ }
11897
+ }
11898
+
11748
11899
  // src/lib/agent-discovery.ts
11749
- var digest = (text2) => createHash5("sha256").update(text2).digest("hex");
11900
+ var digest = (text2) => createHash7("sha256").update(text2).digest("hex");
11750
11901
  function parseConfig(text2, path, toml = false) {
11751
11902
  try {
11752
11903
  const value = toml ? Bun.TOML.parse(text2) : JSON.parse(text2);
@@ -11757,28 +11908,35 @@ function parseConfig(text2, path, toml = false) {
11757
11908
  throw new Error(`Invalid native discovery configuration: ${path}`);
11758
11909
  }
11759
11910
  }
11760
- function safe(path) {
11761
- if (!isAbsolute4(path) || path.includes("\x00"))
11911
+ function safe3(path) {
11912
+ if (!isAbsolute6(path) || path.includes("\x00"))
11762
11913
  throw new Error("Expected an absolute native discovery path");
11763
- for (let cursor = path;; cursor = dirname6(cursor)) {
11764
- if (lstatSync7(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
11914
+ for (let cursor = path;; cursor = dirname8(cursor)) {
11915
+ if (lstatSync9(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
11765
11916
  throw new Error(`Refusing symlink native discovery input: ${cursor}`);
11766
- if (dirname6(cursor) === cursor)
11917
+ if (dirname8(cursor) === cursor)
11767
11918
  break;
11768
11919
  }
11769
11920
  }
11770
11921
  function read(path, changes) {
11771
- safe(path);
11922
+ safe3(path);
11772
11923
  if (changes?.has(path))
11773
11924
  return changes.get(path);
11774
- const stat = lstatSync7(path, { throwIfNoEntry: false });
11925
+ const stat = lstatSync9(path, { throwIfNoEntry: false });
11775
11926
  if (!stat)
11776
11927
  return null;
11777
11928
  if (!stat.isFile() || stat.size > 16 * 1024 * 1024)
11778
11929
  throw new Error(`Unsupported or oversized native discovery input: ${path}`);
11779
11930
  return readFileSync10(path, "utf8");
11780
11931
  }
11781
- function projected(source, changes) {
11932
+ function projected(source, changes, budget = discoveryByteBudget()) {
11933
+ if (source.hashMode !== undefined) {
11934
+ if (source.hashMode !== "bytes")
11935
+ throw new Error("Invalid native discovery hash mode");
11936
+ if (source.format !== undefined || source.fields !== undefined)
11937
+ throw new Error("Raw discovery witnesses cannot project configuration fields");
11938
+ return hashRawDiscoveryFile(source.path, budget, changes);
11939
+ }
11782
11940
  const text2 = read(source.path, changes);
11783
11941
  if (text2 === null)
11784
11942
  return null;
@@ -11792,22 +11950,34 @@ function projected(source, changes) {
11792
11950
  function verifyAgentDiscovery(binding) {
11793
11951
  if (!binding || !Array.isArray(binding.sources) || !Array.isArray(binding.roots) || binding.sources.length > AGENT_POLICY_LIMITS.discoverySources || binding.roots.length > AGENT_POLICY_LIMITS.discoveryRoots)
11794
11952
  throw new Error("Invalid native discovery binding");
11953
+ if (binding.agent === "hermes" && !binding.directories?.length)
11954
+ throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
11955
+ if (binding.directories !== undefined)
11956
+ verifyDiscoveryDirectories(binding.directories);
11957
+ const budget = discoveryByteBudget();
11795
11958
  for (const source of binding.sources) {
11796
11959
  if (source.format !== undefined && (!["json", "toml", "yaml"].includes(source.format) || !Array.isArray(source.fields) || !source.fields.length || source.fields.length > 64 || source.fields.some((field) => typeof field !== "string" || !field)))
11797
11960
  throw new Error("Invalid native discovery projection");
11798
11961
  if (source.sha256 !== null && !/^[a-f0-9]{64}$/.test(source.sha256))
11799
11962
  throw new Error("Invalid native discovery digest");
11800
- if (projected(source) !== source.sha256)
11963
+ if (projected(source, undefined, budget) !== source.sha256)
11801
11964
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
11802
11965
  }
11803
11966
  for (const root of binding.roots)
11804
- safe(root);
11967
+ safe3(root);
11805
11968
  }
11806
11969
  function rebindAgentDiscovery(binding, changes) {
11807
- return { ...binding, sources: binding.sources.map((source) => ({ ...source, sha256: projected(source, changes) })) };
11970
+ const budget = discoveryByteBudget();
11971
+ return { ...binding, sources: binding.sources.map((source) => ({ ...source, sha256: projected(source, changes, budget) })) };
11972
+ }
11973
+ function captureDiscoveryByteSources(paths) {
11974
+ if (!Array.isArray(paths) || paths.length > AGENT_POLICY_LIMITS.discoverySources || new Set(paths).size !== paths.length)
11975
+ throw new Error("Invalid raw discovery source collection");
11976
+ const budget = discoveryByteBudget();
11977
+ return paths.map((path) => ({ path, hashMode: "bytes", sha256: hashRawDiscoveryFile(path, budget) }));
11808
11978
  }
11809
11979
  function resolveAgentDiscovery(options) {
11810
- const canonical = options.canonical ?? resolve5, home = resolve5(options.home), agent = options.agent;
11980
+ const canonical = options.canonical ?? resolve7, home = resolve7(options.home), agent = options.agent;
11811
11981
  const sources = [], roots = new Set, builtinNames = [];
11812
11982
  const witness = (path, format, fields) => {
11813
11983
  const source = { path: canonical(path), sha256: null, ...format ? { format, fields } : {} };
@@ -11817,14 +11987,14 @@ function resolveAgentDiscovery(options) {
11817
11987
  };
11818
11988
  if (agent === "hermes")
11819
11989
  assertHermesEnvironment(home);
11820
- const configPath = join14(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
11990
+ const configPath = join15(home, agent === "hermes" ? ".hermes/config.yaml" : agent === "opencode" ? ".config/opencode/opencode.json" : `.${agent}/${agent === "codex" ? "config.toml" : agent === "cursor" ? "hooks.json" : "settings.json"}`);
11821
11991
  const configText = witness(configPath, agent === "hermes" ? "yaml" : agent === "codex" ? "toml" : "json", agent === "hermes" ? ["skills", "plugins", "hooks"] : agent === "claude" ? ["enabledPlugins", "extraKnownMarketplaces"] : agent === "codex" ? ["plugins", "marketplaces", "skills"] : agent === "gemini" ? ["skills", "extensions", "security"] : agent === "opencode" ? ["plugin", "skills"] : ["version"]);
11822
11992
  const config = configText === null ? {} : agent === "hermes" ? parseHermesConfig(configText) : parseConfig(configText, configPath, agent === "codex");
11823
11993
  const unresolved = (detail) => {
11824
11994
  throw new Error(`Native discovery is unresolved (${agent}: ${detail}); provide a reviewed --discovery-inputs file`);
11825
11995
  };
11826
11996
  if (agent === "hermes") {
11827
- const profile = witness(join14(home, ".hermes/active_profile"));
11997
+ const profile = witness(join15(home, ".hermes/active_profile"));
11828
11998
  if (profile?.trim() && profile.trim() !== "default")
11829
11999
  unresolved("a non-default profile is active");
11830
12000
  const declared = config.skills?.external_dirs ?? [], paths = typeof declared === "string" ? [declared] : declared;
@@ -11838,8 +12008,8 @@ function resolveAgentDiscovery(options) {
11838
12008
  continue;
11839
12009
  if (normalized.startsWith("~") && normalized !== "~" && !normalized.startsWith("~/"))
11840
12010
  unresolved("user-specific tilde expansion requires a separate discovery adapter");
11841
- const path = normalized === "~" ? home : normalized.startsWith("~/") ? join14(home, normalized.slice(2)) : resolve5(home, ".hermes", normalized);
11842
- safe(path);
12011
+ const path = normalized === "~" ? home : normalized.startsWith("~/") ? join15(home, normalized.slice(2)) : resolve7(home, ".hermes", normalized);
12012
+ safe3(path);
11843
12013
  roots.add(path);
11844
12014
  }
11845
12015
  }
@@ -11847,24 +12017,24 @@ function resolveAgentDiscovery(options) {
11847
12017
  const executable = Bun.which("gemini");
11848
12018
  if (executable) {
11849
12019
  let packageRoot;
11850
- for (let directory = dirname6(realpathSync3(executable)), depth = 0;depth < 8; directory = dirname6(directory), depth++) {
11851
- const path = join14(directory, "package.json"), raw = read(path);
12020
+ for (let directory = dirname8(realpathSync3(executable)), depth = 0;depth < 8; directory = dirname8(directory), depth++) {
12021
+ const path = join15(directory, "package.json"), raw = read(path);
11852
12022
  if (raw !== null && parseConfig(raw, path).name === "@google/gemini-cli") {
11853
12023
  packageRoot = directory;
11854
12024
  witness(path);
11855
12025
  break;
11856
12026
  }
11857
- if (dirname6(directory) === directory)
12027
+ if (dirname8(directory) === directory)
11858
12028
  break;
11859
12029
  }
11860
12030
  if (!packageRoot)
11861
12031
  unresolved("installed Gemini package layout is not recognized");
11862
- const builtinRoot = join14(packageRoot, "bundle/builtin");
11863
- safe(builtinRoot);
11864
- if (!lstatSync7(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
12032
+ const builtinRoot = join15(packageRoot, "bundle/builtin");
12033
+ safe3(builtinRoot);
12034
+ if (!lstatSync9(builtinRoot, { throwIfNoEntry: false })?.isDirectory())
11865
12035
  unresolved("installed Gemini builtin root is missing");
11866
12036
  for (const name of readdirSync9(builtinRoot).sort()) {
11867
- const text2 = witness(join14(builtinRoot, name, "SKILL.md"));
12037
+ const text2 = witness(join15(builtinRoot, name, "SKILL.md"));
11868
12038
  const match = text2?.match(/^---\r?\n[\s\S]*?^name:\s*([a-z0-9][a-z0-9-]*)\s*$/m);
11869
12039
  if (!match?.[1] || builtinNames.includes(match[1]))
11870
12040
  unresolved("installed Gemini builtin name is missing or ambiguous");
@@ -11878,7 +12048,7 @@ function resolveAgentDiscovery(options) {
11878
12048
  if (review) {
11879
12049
  if (review.pluginHooks !== "reviewed-no-skill-injection" || !Array.isArray(review.sources) || !review.sources.length || !Array.isArray(review.roots))
11880
12050
  throw new Error("Discovery review must bind sources and confirm plugin hooks do not inject retired skills");
11881
- const supplied = { agent, roots: review.roots, sources: review.sources, method: "reviewed" };
12051
+ const supplied = { agent, roots: review.roots, sources: review.sources, ...review.directories !== undefined ? { directories: review.directories } : {}, method: "reviewed" };
11882
12052
  if (review.sources.some((source) => source.format !== undefined || source.fields !== undefined))
11883
12053
  throw new Error("Explicit discovery reviews require full source-file hashes");
11884
12054
  verifyAgentDiscovery(supplied);
@@ -11888,40 +12058,40 @@ function resolveAgentDiscovery(options) {
11888
12058
  }
11889
12059
  function plugin(root) {
11890
12060
  root = canonical(root);
11891
- safe(root);
11892
- const manifestPath = join14(root, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
12061
+ safe3(root);
12062
+ const manifestPath = join15(root, agent === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json");
11893
12063
  const raw = witness(manifestPath);
11894
12064
  if (raw === null)
11895
12065
  unresolved("plugin manifest missing");
11896
12066
  const manifest = parseConfig(raw, manifestPath);
11897
- const hooks = witness(join14(root, "hooks/hooks.json"));
12067
+ const hooks = witness(join15(root, "hooks/hooks.json"));
11898
12068
  if (manifest.hooks !== undefined || hooks !== null)
11899
12069
  unresolved("plugin hooks require a separate no-skill-injection review");
11900
- const commands = join14(root, "commands");
11901
- safe(commands);
11902
- if (agent === "claude" && (manifest.commands !== undefined || lstatSync7(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length))
12070
+ const commands = join15(root, "commands");
12071
+ safe3(commands);
12072
+ if (agent === "claude" && (manifest.commands !== undefined || lstatSync9(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length))
11903
12073
  unresolved("legacy command discovery requires retirement or a dedicated format adapter");
11904
- const rootSkill = witness(join14(root, "SKILL.md"));
12074
+ const rootSkill = witness(join15(root, "SKILL.md"));
11905
12075
  if (rootSkill !== null)
11906
12076
  roots.add(root);
11907
12077
  const declared = manifest.skills === undefined ? ["./skills"] : typeof manifest.skills === "string" ? [manifest.skills] : manifest.skills;
11908
12078
  if (!Array.isArray(declared) || declared.some((path) => typeof path !== "string"))
11909
12079
  unresolved("unsupported plugin skill paths");
11910
12080
  if (agent === "claude")
11911
- roots.add(join14(root, "skills"));
12081
+ roots.add(join15(root, "skills"));
11912
12082
  for (const value of declared) {
11913
- const path = resolve5(root, value);
12083
+ const path = resolve7(root, value);
11914
12084
  if (path !== root && !path.startsWith(root + sep3))
11915
12085
  unresolved("plugin skill path escapes its root");
11916
- roots.add(path.endsWith(`${sep3}SKILL.md`) ? dirname6(path) : path);
12086
+ roots.add(path.endsWith(`${sep3}SKILL.md`) ? dirname8(path) : path);
11917
12087
  }
11918
12088
  }
11919
12089
  if (agent === "claude") {
11920
- const registrations = witness(join14(home, ".claude/plugins/installed_plugins.json"));
11921
- const installed = registrations === null ? {} : parseConfig(registrations, join14(home, ".claude/plugins/installed_plugins.json")).plugins;
11922
- const commands = canonical(join14(home, ".claude/commands"));
11923
- safe(commands);
11924
- if (lstatSync7(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length)
12090
+ const registrations = witness(join15(home, ".claude/plugins/installed_plugins.json"));
12091
+ const installed = registrations === null ? {} : parseConfig(registrations, join15(home, ".claude/plugins/installed_plugins.json")).plugins;
12092
+ const commands = canonical(join15(home, ".claude/commands"));
12093
+ safe3(commands);
12094
+ if (lstatSync9(commands, { throwIfNoEntry: false })?.isDirectory() && readdirSync9(commands).length)
11925
12095
  unresolved("legacy home command discovery requires a dedicated format adapter");
11926
12096
  for (const [id, enabled] of Object.entries(config.enabledPlugins ?? {})) {
11927
12097
  if (enabled === false)
@@ -11941,34 +12111,34 @@ function resolveAgentDiscovery(options) {
11941
12111
  unresolved("unsupported plugin enablement");
11942
12112
  const split = id.lastIndexOf("@"), name = id.slice(0, split), marketplace = id.slice(split + 1);
11943
12113
  const market = config.marketplaces?.[marketplace] ?? (marketplace === "personal" ? { source_type: "local", source: home } : undefined);
11944
- if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute4(market.source))
12114
+ if (split < 1 || market?.source_type !== "local" || typeof market.source !== "string" || !isAbsolute6(market.source))
11945
12115
  unresolved("enabled plugin marketplace is not an explicit local source");
11946
- const catalogText = witness(join14(market.source, ".agents/plugins/marketplace.json"));
12116
+ const catalogText = witness(join15(market.source, ".agents/plugins/marketplace.json"));
11947
12117
  if (catalogText === null)
11948
12118
  unresolved("marketplace catalog missing");
11949
- const catalog = parseConfig(catalogText, join14(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
12119
+ const catalog = parseConfig(catalogText, join15(market.source, ".agents/plugins/marketplace.json")), matches = Array.isArray(catalog.plugins) ? catalog.plugins.filter((item) => item.name === name) : [];
11950
12120
  if (matches.length !== 1 || matches[0]?.source?.source !== "local" || typeof matches[0]?.source?.path !== "string")
11951
12121
  unresolved("plugin catalog source is missing or ambiguous");
11952
- plugin(resolve5(market.source, matches[0].source.path));
12122
+ plugin(resolve7(market.source, matches[0].source.path));
11953
12123
  }
11954
12124
  } else if (agent === "opencode") {
11955
- if (witness(join14(home, ".config/opencode/opencode.jsonc")) !== null || witness(join14(home, ".config/opencode/config.json")) !== null)
12125
+ if (witness(join15(home, ".config/opencode/opencode.jsonc")) !== null || witness(join15(home, ".config/opencode/config.json")) !== null)
11956
12126
  unresolved("additional config layers require review");
11957
12127
  if ((config.plugin?.length ?? 0) > 0 || config.skills?.urls?.length)
11958
12128
  unresolved("external plugin or remote skill source");
11959
12129
  for (const path of config.skills?.paths ?? []) {
11960
- if (typeof path !== "string" || !isAbsolute4(path))
12130
+ if (typeof path !== "string" || !isAbsolute6(path))
11961
12131
  unresolved("relative or malformed added skill root");
11962
12132
  roots.add(path);
11963
12133
  }
11964
- const localPlugins = join14(home, ".config/opencode/plugins");
11965
- safe(localPlugins);
11966
- if (lstatSync7(localPlugins, { throwIfNoEntry: false }) && readdirSync9(localPlugins).some((name) => name !== "skills-cli.js"))
12134
+ const localPlugins = join15(home, ".config/opencode/plugins");
12135
+ safe3(localPlugins);
12136
+ if (lstatSync9(localPlugins, { throwIfNoEntry: false }) && readdirSync9(localPlugins).some((name) => name !== "skills-cli.js"))
11967
12137
  unresolved("additional local plugin hooks require review");
11968
12138
  } else if (agent === "hermes") {
11969
- for (const directory of [join14(home, ".hermes/plugins"), join14(home, ".hermes/hermes-agent")]) {
11970
- safe(directory);
11971
- if (lstatSync7(directory, { throwIfNoEntry: false }))
12139
+ for (const directory of [join15(home, ".hermes/plugins"), join15(home, ".hermes/hermes-agent")]) {
12140
+ safe3(directory);
12141
+ if (lstatSync9(directory, { throwIfNoEntry: false }))
11972
12142
  unresolved("installed runtime/plugin discovery requires reviewed source bindings");
11973
12143
  }
11974
12144
  if (Bun.which("hermes") || Object.keys(config.plugins ?? {}).length)
@@ -11976,24 +12146,24 @@ function resolveAgentDiscovery(options) {
11976
12146
  } else if (agent === "gemini") {
11977
12147
  if (Object.keys(config.extensions ?? {}).length || config.skills?.paths?.length)
11978
12148
  unresolved("custom extension or skill paths");
11979
- const directory = join14(home, ".gemini/extensions");
11980
- safe(directory);
11981
- if (lstatSync7(directory, { throwIfNoEntry: false }))
12149
+ const directory = join15(home, ".gemini/extensions");
12150
+ safe3(directory);
12151
+ if (lstatSync9(directory, { throwIfNoEntry: false }))
11982
12152
  for (const name of readdirSync9(directory)) {
11983
- const root = join14(directory, name);
11984
- safe(root);
11985
- if (!lstatSync7(root)?.isDirectory())
12153
+ const root = join15(directory, name);
12154
+ safe3(root);
12155
+ if (!lstatSync9(root)?.isDirectory())
11986
12156
  continue;
11987
- const raw = witness(join14(root, "gemini-extension.json"));
12157
+ const raw = witness(join15(root, "gemini-extension.json"));
11988
12158
  if (raw === null)
11989
12159
  unresolved("extension registration missing");
11990
- const manifest = parseConfig(raw, join14(root, "gemini-extension.json"));
11991
- if (manifest.hooks !== undefined || witness(join14(root, "hooks/hooks.json")) !== null || witness(join14(root, ".gemini-extension-install.json")) !== null)
12160
+ const manifest = parseConfig(raw, join15(root, "gemini-extension.json"));
12161
+ if (manifest.hooks !== undefined || witness(join15(root, "hooks/hooks.json")) !== null || witness(join15(root, ".gemini-extension-install.json")) !== null)
11992
12162
  unresolved("extension hooks or linked registration require review");
11993
- roots.add(join14(root, "skills"));
12163
+ roots.add(join15(root, "skills"));
11994
12164
  }
11995
12165
  }
11996
- return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {} };
12166
+ return { agent, roots: [...roots].sort(), sources, method: "automatic", ...agent === "gemini" ? { builtinNames } : {}, ...agent === "hermes" ? { directories: captureDiscoveryDirectories([join15(home, ".hermes/plugins"), join15(home, ".hermes/hermes-agent")].map((path) => canonical(path))) } : {} };
11997
12167
  }
11998
12168
 
11999
12169
  // src/lib/agent-adapters.ts
@@ -12058,7 +12228,7 @@ export default async ({ directory }) => {
12058
12228
  }
12059
12229
 
12060
12230
  // src/lib/agent-integration.ts
12061
- var sha = (data) => createHash6("sha256").update(data).digest("hex");
12231
+ var sha = (data) => createHash8("sha256").update(data).digest("hex");
12062
12232
  var ROOTS = [
12063
12233
  ["claude", ".claude/skills"],
12064
12234
  ["codex", ".codex/skills"],
@@ -12077,11 +12247,11 @@ var ROOTS = [
12077
12247
  ["copilot", ".github/skills"]
12078
12248
  ];
12079
12249
  function assertSafePath(path) {
12080
- let cursor = resolve6(path);
12250
+ let cursor = resolve8(path);
12081
12251
  while (true) {
12082
- if (lstatSync8(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
12252
+ if (lstatSync10(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
12083
12253
  throw new Error(`Refusing symlink path: ${cursor}`);
12084
- const parent = dirname7(cursor);
12254
+ const parent = dirname9(cursor);
12085
12255
  if (parent === cursor)
12086
12256
  return;
12087
12257
  cursor = parent;
@@ -12089,17 +12259,17 @@ function assertSafePath(path) {
12089
12259
  }
12090
12260
  function readRootAlias(home, agent) {
12091
12261
  assertSafePath(home);
12092
- const realHome = realpathSync4(home), alias = join15(realHome, `.${agent}`);
12093
- const stat = lstatSync8(alias, { throwIfNoEntry: false, bigint: true });
12262
+ const realHome = realpathSync4(home), alias = join16(realHome, `.${agent}`);
12263
+ const stat = lstatSync10(alias, { throwIfNoEntry: false, bigint: true });
12094
12264
  if (!stat?.isSymbolicLink())
12095
12265
  throw new Error(`Agent root alias changed after planning: ${alias}`);
12096
- const link = readlinkSync(alias), targetPath = resolve6(dirname7(alias), link);
12266
+ const link = readlinkSync(alias), targetPath = resolve8(dirname9(alias), link);
12097
12267
  assertSafePath(targetPath);
12098
- const targetStat = lstatSync8(targetPath, { throwIfNoEntry: false });
12268
+ const targetStat = lstatSync10(targetPath, { throwIfNoEntry: false });
12099
12269
  if (!targetStat?.isDirectory())
12100
12270
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
12101
12271
  const target = realpathSync4(targetPath), inside = relative4(realHome, target);
12102
- if (!inside || inside === ".." || inside.startsWith(`..${sep4}`) || isAbsolute5(inside))
12272
+ if (!inside || inside === ".." || inside.startsWith(`..${sep4}`) || isAbsolute7(inside))
12103
12273
  throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
12104
12274
  return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat.dev}:${stat.ino}:${stat.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
12105
12275
  }
@@ -12108,19 +12278,19 @@ function rootAliases(home, allowed = false) {
12108
12278
  return [];
12109
12279
  const result = [];
12110
12280
  for (const agent of ["claude", "codex"])
12111
- if (lstatSync8(join15(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
12281
+ if (lstatSync10(join16(home, `.${agent}`), { throwIfNoEntry: false })?.isSymbolicLink())
12112
12282
  result.push(readRootAlias(home, agent));
12113
12283
  if (result.some((item, index) => result.slice(index + 1).some((other) => item.target === other.target || item.target.startsWith(other.target + sep4) || other.target.startsWith(item.target + sep4))))
12114
12284
  throw new Error("Agent root alias targets must not overlap");
12115
12285
  return result;
12116
12286
  }
12117
12287
  function canonicalAgentPath(path, aliases) {
12118
- const absolute = resolve6(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep4));
12119
- return binding ? join15(binding.target, relative4(binding.alias, absolute)) : absolute;
12288
+ const absolute = resolve8(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep4));
12289
+ return binding ? join16(binding.target, relative4(binding.alias, absolute)) : absolute;
12120
12290
  }
12121
12291
  function recheckRootAliases(aliases) {
12122
12292
  for (const binding of aliases) {
12123
- if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join15(binding.home, `.${binding.agent}`))
12293
+ if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join16(binding.home, `.${binding.agent}`))
12124
12294
  throw new Error("Invalid agent root alias binding");
12125
12295
  const current = readRootAlias(binding.home, binding.agent);
12126
12296
  if (JSON.stringify(current) !== JSON.stringify(binding))
@@ -12129,42 +12299,42 @@ function recheckRootAliases(aliases) {
12129
12299
  }
12130
12300
  function readNativeBytes(path, maximum = 64 * 1024 * 1024) {
12131
12301
  assertSafePath(path);
12132
- const before = lstatSync8(path);
12302
+ const before = lstatSync10(path);
12133
12303
  if (!before.isFile() || before.size > maximum)
12134
12304
  throw new Error("Unsupported or oversized native skill file");
12135
- const descriptor = openSync3(path, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
12305
+ const descriptor = openSync4(path, constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
12136
12306
  try {
12137
- const opened = fstatSync3(descriptor);
12307
+ const opened = fstatSync4(descriptor);
12138
12308
  if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size)
12139
12309
  throw new Error("Native skill file changed while reading");
12140
12310
  const bytes = Buffer.allocUnsafe(before.size + 1);
12141
12311
  let length = 0;
12142
12312
  while (length < bytes.length) {
12143
- const count = readSync3(descriptor, bytes, length, bytes.length - length, null);
12313
+ const count = readSync4(descriptor, bytes, length, bytes.length - length, null);
12144
12314
  if (!count)
12145
12315
  break;
12146
12316
  length += count;
12147
12317
  }
12148
- const after = fstatSync3(descriptor);
12318
+ const after = fstatSync4(descriptor);
12149
12319
  if (length !== before.size || after.size !== before.size || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs)
12150
12320
  throw new Error("Native skill file changed while reading");
12151
12321
  return bytes.subarray(0, length);
12152
12322
  } finally {
12153
- closeSync3(descriptor);
12323
+ closeSync4(descriptor);
12154
12324
  }
12155
12325
  }
12156
12326
  function treeHash(root) {
12157
- const hash2 = createHash6("sha256");
12327
+ const hash2 = createHash8("sha256");
12158
12328
  let bytes = 0, files = 0;
12159
12329
  function visit2(path) {
12160
- const stat = lstatSync8(path);
12330
+ const stat = lstatSync10(path);
12161
12331
  if (stat.isSymbolicLink())
12162
12332
  throw new Error(`Refusing symlink in skill: ${path}`);
12163
12333
  const rel = relative4(root, path);
12164
12334
  if (stat.isDirectory()) {
12165
12335
  hash2.update(`d\x00${rel}\x00`);
12166
12336
  for (const name of readdirSync10(path).sort())
12167
- visit2(join15(path, name));
12337
+ visit2(join16(path, name));
12168
12338
  return;
12169
12339
  }
12170
12340
  if (!stat.isFile())
@@ -12182,37 +12352,50 @@ function treeHash(root) {
12182
12352
  }
12183
12353
  function inventoryNativeSkills(home = homedir2(), options = {}) {
12184
12354
  const aliases = rootAliases(home, options.allowRootAliases);
12185
- const roots = ROOTS.map(([agent, path]) => [agent, canonicalAgentPath(join15(home, path), aliases)]);
12186
- const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(join15(home, adapter.root, CLI_BRIDGE_NAME), aliases));
12187
- for (const project of new Set([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []].map((path) => resolve6(path)))) {
12355
+ const roots = ROOTS.map(([agent, path]) => [agent, canonicalAgentPath(join16(home, path), aliases)]);
12356
+ const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(join16(home, adapter.root, CLI_BRIDGE_NAME), aliases));
12357
+ for (const project of new Set([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []].map((path) => resolve8(path)))) {
12188
12358
  for (const [agent, path] of ROOTS)
12189
- roots.push([agent, canonicalAgentPath(join15(project, path), aliases)]);
12359
+ roots.push([agent, canonicalAgentPath(join16(project, path), aliases)]);
12190
12360
  }
12191
12361
  const entries = [], seen = new Set;
12192
12362
  const cacheScans = new Map, maxAliasProofEntries = 1e4;
12363
+ let discoveryEntries = 0, discoveryPathBytes = 0;
12364
+ function admit(path) {
12365
+ if (++discoveryEntries > 20000)
12366
+ throw new Error("Native skill discovery entry limit exceeded");
12367
+ discoveryPathBytes += Buffer.byteLength(path, "utf8");
12368
+ if (discoveryPathBytes > 4 * 1024 * 1024)
12369
+ throw new Error("Native skill discovery metadata limit exceeded");
12370
+ }
12193
12371
  function emptySiblingCacheAlias(path, parent) {
12194
- const link = readlinkSync(path), target = resolve6(parent, link);
12195
- if (dirname7(target) !== parent || (isAbsolute5(link) ? link !== target : dirname7(link) !== "."))
12372
+ const link = readlinkSync(path), target = resolve8(parent, link);
12373
+ if (dirname9(target) !== parent || (isAbsolute7(link) ? link !== target : dirname9(link) !== "."))
12196
12374
  return false;
12197
- if (!lstatSync8(target, { throwIfNoEntry: false })?.isDirectory())
12375
+ if (!lstatSync10(target, { throwIfNoEntry: false })?.isDirectory())
12198
12376
  return false;
12199
12377
  const scan = cacheScans.get(target);
12200
12378
  return Boolean(scan?.complete && !scan.hasSkills && scan.entries <= maxAliasProofEntries);
12201
12379
  }
12202
- function visit2(agent, path, vendor = false, depth = 0, pluginCache = false) {
12380
+ function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false) {
12381
+ if (!admitted)
12382
+ admit(path);
12203
12383
  const scan = { complete: true, hasSkills: false, entries: 1 };
12204
12384
  assertSafePath(path);
12205
12385
  if (!existsSync10(path))
12206
12386
  return scan;
12207
- if (!lstatSync8(path).isDirectory())
12387
+ const stat = lstatSync10(path);
12388
+ if (stat.isFile())
12208
12389
  return scan;
12209
- if (existsSync10(join15(path, "SKILL.md"))) {
12390
+ if (!stat.isDirectory())
12391
+ throw new Error(`Unsupported native discovery entry: ${path}`);
12392
+ if (existsSync10(join16(path, "SKILL.md"))) {
12210
12393
  scan.hasSkills = true;
12211
12394
  if (seen.has(path))
12212
12395
  return scan;
12213
12396
  seen.add(path);
12214
12397
  let managed = false;
12215
- const marker = join15(path, ".hasna-skills.json");
12398
+ const marker = join16(path, ".hasna-skills.json");
12216
12399
  if (existsSync10(marker)) {
12217
12400
  try {
12218
12401
  managed = JSON.parse(readFileSync11(marker, "utf8")).managedBy === "@hasna/skills";
@@ -12220,12 +12403,22 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
12220
12403
  }
12221
12404
  const bridge = !vendor && isOwnedCliBridge(path, bridgePaths);
12222
12405
  const rootAlias = aliases.find((item) => path === item.target || path.startsWith(item.target + sep4));
12223
- entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(join15(home, ".codex", "skills", ".system"), aliases) + sep4) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve6(home) } : {}, ...rootAlias ? { rootAlias } : {} });
12406
+ entries.push({ agent, path, hash: treeHash(path), managed, vendor, ...agent === "codex" && path.startsWith(canonicalAgentPath(join16(home, ".codex", "skills", ".system"), aliases) + sep4) ? { system: true } : {}, ...bridge ? { bridge: true, bridgeHome: resolve8(home) } : {}, ...rootAlias ? { rootAlias } : {} });
12224
12407
  return scan;
12225
12408
  }
12226
- if (depth > (vendor ? 8 : 3))
12409
+ if (depth > (vendor ? 32 : 3))
12227
12410
  return { ...scan, complete: false };
12228
- const children = readdirSync10(path, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
12411
+ const children = [], directory = opendirSync3(path);
12412
+ try {
12413
+ let child;
12414
+ while ((child = directory.readSync()) !== null) {
12415
+ admit(join16(path, child.name));
12416
+ children.push(child);
12417
+ }
12418
+ } finally {
12419
+ directory.closeSync();
12420
+ }
12421
+ children.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
12229
12422
  if (pluginCache)
12230
12423
  children.sort((a, b) => Number(a.isSymbolicLink()) - Number(b.isSymbolicLink()));
12231
12424
  for (const { name } of children) {
@@ -12236,15 +12429,15 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
12236
12429
  const isVendor = vendor || name === ".system";
12237
12430
  if (isVendor && !options.includeVendor)
12238
12431
  continue;
12239
- const child = join15(path, name);
12240
- if (isVendor && lstatSync8(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
12432
+ const child = join16(path, name);
12433
+ if (isVendor && lstatSync10(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
12241
12434
  const target = statSync8(child, { throwIfNoEntry: false });
12242
12435
  if (target?.isFile() || pluginCache && target?.isDirectory() && emptySiblingCacheAlias(child, path)) {
12243
12436
  scan.entries++;
12244
12437
  continue;
12245
12438
  }
12246
12439
  }
12247
- const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache);
12440
+ const childScan = visit2(agent, child, isVendor, depth + 1, pluginCache, true);
12248
12441
  scan.complete &&= childScan.complete;
12249
12442
  scan.hasSkills ||= childScan.hasSkills;
12250
12443
  scan.entries += childScan.entries;
@@ -12263,8 +12456,8 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
12263
12456
  scanRoot(agent, path);
12264
12457
  if (options.includeVendor) {
12265
12458
  for (const agent of ["codex", "claude"])
12266
- scanRoot(agent, canonicalAgentPath(join15(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
12267
- scanRoot("gemini", join15(home, ".gemini", "extensions"), true);
12459
+ scanRoot(agent, canonicalAgentPath(join16(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
12460
+ scanRoot("gemini", join16(home, ".gemini", "extensions"), true);
12268
12461
  }
12269
12462
  const configured = options.configured ? INTEGRATION_AGENTS.flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
12270
12463
  for (const root of [...options.agentRoots ?? [], ...configured])
@@ -12362,12 +12555,12 @@ function disableCodexSkills(text2, skills, aliases, bridgePath) {
12362
12555
  let result = text2;
12363
12556
  const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
12364
12557
  for (const skill of selections) {
12365
- const path = join15(skill.path, "SKILL.md");
12558
+ const path = join16(skill.path, "SKILL.md");
12366
12559
  let found = false;
12367
12560
  result = result.replace(/^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm, (section) => {
12368
12561
  const parsed = Bun.TOML.parse(section);
12369
12562
  const declared = parsed.skills?.config?.[0]?.path;
12370
- if (!declared || ![resolve6(path), resolve6(skill.path)].includes(canonicalAgentPath(declared, aliases)))
12563
+ if (!declared || ![resolve8(path), resolve8(skill.path)].includes(canonicalAgentPath(declared, aliases)))
12371
12564
  return section;
12372
12565
  found = true;
12373
12566
  return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
@@ -12391,7 +12584,7 @@ function planAgentIntegration(options) {
12391
12584
  if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(profileId) || profileId.includes(".."))
12392
12585
  throw new Error("Invalid selection profile id");
12393
12586
  const aliases = rootAliases(home, options.allowRootAliases);
12394
- const policyPath = join15(dataDir2, "agent-policy.json");
12587
+ const policyPath = join16(dataDir2, "agent-policy.json");
12395
12588
  assertSafePath(policyPath);
12396
12589
  const priorSnapshot = readManagedSkillPolicySnapshot(dataDir2), previousPolicy = priorSnapshot?.text ?? null, policy = priorSnapshot?.value ?? {};
12397
12590
  if (policy.bridge !== undefined && (!policy.bridge || typeof policy.bridge !== "object" || Array.isArray(policy.bridge)))
@@ -12408,19 +12601,19 @@ function planAgentIntegration(options) {
12408
12601
  if (!INTEGRATION_AGENTS.includes(agent))
12409
12602
  throw new Error(`Unsupported agent: ${agent}`);
12410
12603
  const adapter = AGENT_ADAPTERS[agent];
12411
- const bridgePath = canonicalAgentPath(join15(home, adapter.root, CLI_BRIDGE_NAME), aliases);
12604
+ const bridgePath = canonicalAgentPath(join16(home, adapter.root, CLI_BRIDGE_NAME), aliases);
12412
12605
  assertSafePath(bridgePath);
12413
12606
  if (existsSync10(bridgePath) && !isOwnedCliBridge(bridgePath, [bridgePath]))
12414
12607
  throw new Error(`Refusing to overwrite an unrecognized or modified Skills bridge: ${bridgePath}`);
12415
12608
  for (const [name, after2] of Object.entries(CLI_BRIDGE_FILES)) {
12416
- const path2 = join15(bridgePath, name), before2 = readOptional(path2);
12609
+ const path2 = join16(bridgePath, name), before2 = readOptional(path2);
12417
12610
  if (before2 !== after2)
12418
12611
  changes.push({ path: path2, before: before2, after: after2 });
12419
12612
  }
12420
- const path = canonicalAgentPath(join15(home, adapter.config), aliases);
12613
+ const path = canonicalAgentPath(join16(home, adapter.config), aliases);
12421
12614
  const before = readOptional(path);
12422
12615
  if (agent === "hermes") {
12423
- const supervisorPath = join15(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(options.command ?? "skills", profileId);
12616
+ const supervisorPath = join16(dataDir2, "agent-hooks", "hermes.js"), supervisorBefore = readOptional(supervisorPath), supervisorAfter = renderHermesSupervisor(options.command ?? "skills", profileId);
12424
12617
  const priorCommand = policy.bridge?.commands?.hermes, priorProfile = policy.bridge?.profiles?.hermes ?? policy.profileId;
12425
12618
  const previous = policy.bridge?.supervisors?.hermes;
12426
12619
  if (supervisorBefore !== null && supervisorBefore !== supervisorAfter && !(previous?.path === supervisorPath && typeof priorCommand === "string" && typeof priorProfile === "string" && supervisorBefore === renderHermesSupervisor(priorCommand, priorProfile)))
@@ -12431,7 +12624,7 @@ function planAgentIntegration(options) {
12431
12624
  const after2 = configureHermesHooks(before, supervisor, previous);
12432
12625
  if (before !== after2)
12433
12626
  changes.push({ path, before, after: after2 });
12434
- const optOut = join15(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
12627
+ const optOut = join16(home, ".hermes", HERMES_OPT_OUT), optOutBefore = readOptional(optOut);
12435
12628
  if (optOutBefore === null)
12436
12629
  changes.push({ path: optOut, before: null, after: `Managed by @hasna/skills: bundled native skill reseeding is disabled.
12437
12630
  ` });
@@ -12443,7 +12636,7 @@ function planAgentIntegration(options) {
12443
12636
  if (!permission || typeof permission !== "object" || Array.isArray(permission))
12444
12637
  throw new Error("Expected OpenCode permission object");
12445
12638
  config.permission = { ...permission, skill: { "*": "deny", [CLI_BRIDGE_NAME]: "allow" } };
12446
- const pluginPath = join15(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
12639
+ const pluginPath = join16(home, ".config", "opencode", "plugins", "skills-cli.js"), pluginBefore = readOptional(pluginPath);
12447
12640
  const pluginAfter = renderOpenCodePlugin(options.command ?? "skills", profileId);
12448
12641
  const priorCommand = policy.bridge?.commands?.opencode;
12449
12642
  const ownedPrevious = typeof priorCommand === "string" && typeof policy.profileId === "string" && pluginBefore === renderOpenCodePlugin(priorCommand, policy.bridge?.profiles?.opencode ?? policy.profileId);
@@ -12460,7 +12653,7 @@ function planAgentIntegration(options) {
12460
12653
  if (before !== after)
12461
12654
  changes.push({ path, before, after });
12462
12655
  if (agent === "codex") {
12463
- const configPath = canonicalAgentPath(join15(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
12656
+ const configPath = canonicalAgentPath(join16(home, ".codex", "config.toml"), aliases), previous = readOptional(configPath);
12464
12657
  const next = disableCodexSkills(previous ?? "", nativeSkills, aliases, bridgePath);
12465
12658
  if (next !== (previous ?? ""))
12466
12659
  changes.push({ path: configPath, before: previous, after: next });
@@ -12473,12 +12666,12 @@ function planAgentIntegration(options) {
12473
12666
  version: CLI_BRIDGE_VERSION,
12474
12667
  digest: CLI_BRIDGE_DIGEST,
12475
12668
  agents: [...new Set([...priorAgents, ...options.agents])].sort(),
12476
- home: resolve6(home),
12669
+ home: resolve8(home),
12477
12670
  includeVendor: true,
12478
- ...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join15(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha(renderHermesSupervisor(options.command ?? "skills", profileId)) } } } : {},
12671
+ ...options.agents.includes("hermes") ? { supervisors: { ...policy.bridge?.supervisors, hermes: { path: join16(dataDir2, "agent-hooks", "hermes.js"), runtime: process.execPath, sha256: sha(renderHermesSupervisor(options.command ?? "skills", profileId)) } } } : {},
12479
12672
  commands: { ...policy.bridge?.commands, ...Object.fromEntries(options.agents.map((agent) => [agent, options.command ?? "skills"])) },
12480
12673
  profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, options.agents.includes(agent) ? profileId : policy.bridge?.profiles?.[agent] ?? policy.profileId])),
12481
- disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join15(home, ".codex", "skills", ".system"), aliases) + sep4)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
12674
+ disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join16(home, ".codex", "skills", ".system"), aliases) + sep4)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
12482
12675
  rootAliases: aliases,
12483
12676
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
12484
12677
  } };
@@ -12490,7 +12683,7 @@ function planAgentIntegration(options) {
12490
12683
  }
12491
12684
  function atomicWrite2(path, content) {
12492
12685
  assertSafePath(path);
12493
- mkdirSync5(dirname7(path), { recursive: true, mode: 448 });
12686
+ mkdirSync5(dirname9(path), { recursive: true, mode: 448 });
12494
12687
  const temporary = `${path}.skills-${randomUUID2()}`;
12495
12688
  try {
12496
12689
  writeFileSync5(temporary, content, { mode: 384, flag: "wx" });
@@ -12501,12 +12694,12 @@ function atomicWrite2(path, content) {
12501
12694
  }
12502
12695
  }
12503
12696
  function applyAgentIntegration(plan) {
12504
- const policyPath = join15(plan.dataDir, "agent-policy.json");
12697
+ const policyPath = join16(plan.dataDir, "agent-policy.json");
12505
12698
  assertSafePath(policyPath);
12506
- const currentText = (path) => resolve6(path) === resolve6(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
12699
+ const currentText = (path) => resolve8(path) === resolve8(policyPath) ? readManagedSkillPolicySnapshot(plan.dataDir)?.text ?? null : readOptional(path);
12507
12700
  readManagedSkillPolicySnapshot(plan.dataDir);
12508
12701
  for (const change of plan.changes)
12509
- if (resolve6(change.path) === resolve6(policyPath))
12702
+ if (resolve8(change.path) === resolve8(policyPath))
12510
12703
  parseManagedSkillPolicy(change.after);
12511
12704
  const aliases = plan.rootAliases ?? [];
12512
12705
  recheckRootAliases(aliases);
@@ -12517,7 +12710,7 @@ function applyAgentIntegration(plan) {
12517
12710
  for (const change of plan.changes)
12518
12711
  if (currentText(change.path) !== change.before)
12519
12712
  throw new Error(`Configuration changed after planning: ${change.path}`);
12520
- const backupRoot = join15(plan.dataDir, "migration", randomUUID2()), backups = [], written = [], createdDirectories = new Set;
12713
+ const backupRoot = join16(plan.dataDir, "migration", randomUUID2()), backups = [], written = [], createdDirectories = new Set;
12521
12714
  if (!plan.changes.length) {
12522
12715
  recheckRootAliases(aliases);
12523
12716
  return { changed: [], backups, ...aliases.length ? { rootAliases: aliases } : {} };
@@ -12527,7 +12720,7 @@ function applyAgentIntegration(plan) {
12527
12720
  chmodSync(backupRoot, 448);
12528
12721
  for (const [index, change] of plan.changes.entries())
12529
12722
  if (change.before !== null) {
12530
- const backup = join15(backupRoot, `${index}-${sha(change.path).slice(0, 12)}.backup`);
12723
+ const backup = join16(backupRoot, `${index}-${sha(change.path).slice(0, 12)}.backup`);
12531
12724
  writeFileSync5(backup, change.before, { mode: 384, flag: "wx" });
12532
12725
  backups.push(backup);
12533
12726
  }
@@ -12536,7 +12729,7 @@ function applyAgentIntegration(plan) {
12536
12729
  recheckRootAliases(aliases);
12537
12730
  if (currentText(change.path) !== change.before)
12538
12731
  throw new Error(`Configuration changed after planning: ${change.path}`);
12539
- for (let directory = dirname7(change.path);!existsSync10(directory); directory = dirname7(directory))
12732
+ for (let directory = dirname9(change.path);!existsSync10(directory); directory = dirname9(directory))
12540
12733
  createdDirectories.add(directory);
12541
12734
  const after = change.after;
12542
12735
  atomicWrite2(change.path, after);
@@ -12545,7 +12738,7 @@ function applyAgentIntegration(plan) {
12545
12738
  recheckRootAliases(aliases);
12546
12739
  for (const binding of plan.discoveryAfter ?? [])
12547
12740
  verifyAgentDiscovery(binding);
12548
- atomicWrite2(join15(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha(change.before), afterHash: sha(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
12741
+ atomicWrite2(join16(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha(change.before), afterHash: sha(change.after) })), backups, ...aliases.length ? { rootAliases: aliases } : {} }) + `
12549
12742
  `);
12550
12743
  } catch (error) {
12551
12744
  for (const change of written.reverse()) {
@@ -12576,11 +12769,11 @@ function applyAgentIntegration(plan) {
12576
12769
  }
12577
12770
  function syncArchiveDirectory(path) {
12578
12771
  assertSafePath(path);
12579
- const descriptor = openSync3(path, constants3.O_RDONLY | constants3.O_DIRECTORY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
12772
+ const descriptor = openSync4(path, constants4.O_RDONLY | constants4.O_DIRECTORY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
12580
12773
  try {
12581
12774
  fsyncSync(descriptor);
12582
12775
  } finally {
12583
- closeSync3(descriptor);
12776
+ closeSync4(descriptor);
12584
12777
  }
12585
12778
  }
12586
12779
  function writeArchiveJournal(path, value) {
@@ -12588,18 +12781,18 @@ function writeArchiveJournal(path, value) {
12588
12781
  const temporary = `${path}.skills-${randomUUID2()}`;
12589
12782
  let descriptor;
12590
12783
  try {
12591
- descriptor = openSync3(temporary, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
12784
+ descriptor = openSync4(temporary, constants4.O_WRONLY | constants4.O_CREAT | constants4.O_EXCL | constants4.O_NOFOLLOW, 384);
12592
12785
  writeFileSync5(descriptor, `${JSON.stringify(value)}
12593
12786
  `);
12594
12787
  fsyncSync(descriptor);
12595
- closeSync3(descriptor);
12788
+ closeSync4(descriptor);
12596
12789
  descriptor = undefined;
12597
12790
  renameSync3(temporary, path);
12598
- syncArchiveDirectory(dirname7(path));
12791
+ syncArchiveDirectory(dirname9(path));
12599
12792
  } finally {
12600
12793
  if (descriptor !== undefined)
12601
- closeSync3(descriptor);
12602
- if (lstatSync8(temporary, { throwIfNoEntry: false }))
12794
+ closeSync4(descriptor);
12795
+ if (lstatSync10(temporary, { throwIfNoEntry: false }))
12603
12796
  unlinkSync2(temporary);
12604
12797
  }
12605
12798
  }
@@ -12610,7 +12803,7 @@ function archiveNativeSkills(inventory, options) {
12610
12803
  recheckRootAliases(aliases);
12611
12804
  for (const entry of inventory.filter((entry2) => entry2.bridge)) {
12612
12805
  const adapter = AGENT_ADAPTERS[entry.agent];
12613
- const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join15(entry.bridgeHome, adapter.root, CLI_BRIDGE_NAME), aliases) : undefined;
12806
+ const expected = adapter && entry.bridgeHome ? canonicalAgentPath(join16(entry.bridgeHome, adapter.root, CLI_BRIDGE_NAME), aliases) : undefined;
12614
12807
  if (!expected || treeHash(entry.path) !== entry.hash || !isOwnedCliBridge(entry.path, [expected]))
12615
12808
  throw new Error(`Skills bridge changed after planning: ${entry.path}`);
12616
12809
  }
@@ -12620,11 +12813,11 @@ function archiveNativeSkills(inventory, options) {
12620
12813
  throw new Error(`Native skill changed after planning: ${entry.path}`);
12621
12814
  if (!selected.length)
12622
12815
  return { entries: [], ...aliases.length ? { rootAliases: aliases } : {} };
12623
- const operationId = randomUUID2(), archiveRoot = join15(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join15(archiveRoot, "receipt.json");
12816
+ const operationId = randomUUID2(), archiveRoot = join16(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join16(archiveRoot, "receipt.json");
12624
12817
  const entries = selected.map((entry, index) => ({
12625
- source: entry.vendor ? join15(entry.path, "SKILL.md") : entry.path,
12626
- archive: join15(archiveRoot, `${index}-${sha(entry.path).slice(0, 12)}`),
12627
- hash: entry.vendor ? sha(readNativeBytes(join15(entry.path, "SKILL.md"))) : entry.hash,
12818
+ source: entry.vendor ? join16(entry.path, "SKILL.md") : entry.path,
12819
+ archive: join16(archiveRoot, `${index}-${sha(entry.path).slice(0, 12)}`),
12820
+ hash: entry.vendor ? sha(readNativeBytes(join16(entry.path, "SKILL.md"))) : entry.hash,
12628
12821
  ...entry.vendor ? { discoveryOnly: true } : {},
12629
12822
  status: "planned"
12630
12823
  }));
@@ -12632,7 +12825,7 @@ function archiveNativeSkills(inventory, options) {
12632
12825
  const moved = [];
12633
12826
  const verifyArchive = (entry) => {
12634
12827
  assertSafePath(entry.archive);
12635
- const stat = lstatSync8(entry.archive);
12828
+ const stat = lstatSync10(entry.archive);
12636
12829
  if (entry.discoveryOnly ? !stat.isFile() : !stat.isDirectory())
12637
12830
  throw new Error("Archive type changed");
12638
12831
  if ((entry.discoveryOnly ? sha(readNativeBytes(entry.archive)) : treeHash(entry.archive)) !== entry.hash)
@@ -12640,14 +12833,14 @@ function archiveNativeSkills(inventory, options) {
12640
12833
  };
12641
12834
  assertSafePath(archiveRoot);
12642
12835
  const created = [];
12643
- for (let path = archiveRoot;!existsSync10(path); path = dirname7(path))
12836
+ for (let path = archiveRoot;!existsSync10(path); path = dirname9(path))
12644
12837
  created.push(path);
12645
12838
  mkdirSync5(archiveRoot, { recursive: true, mode: 448 });
12646
12839
  try {
12647
12840
  for (const directory of created)
12648
12841
  syncArchiveDirectory(directory);
12649
12842
  if (created.length)
12650
- syncArchiveDirectory(dirname7(created.at(-1)));
12843
+ syncArchiveDirectory(dirname9(created.at(-1)));
12651
12844
  writeArchiveJournal(receiptPath, journal);
12652
12845
  for (const [index, entry] of selected.entries()) {
12653
12846
  recheckRootAliases(aliases);
@@ -12659,7 +12852,7 @@ function archiveNativeSkills(inventory, options) {
12659
12852
  writeArchiveJournal(receiptPath, journal);
12660
12853
  renameSync3(move.source, move.archive);
12661
12854
  moved.push(move);
12662
- syncArchiveDirectory(dirname7(move.source));
12855
+ syncArchiveDirectory(dirname9(move.source));
12663
12856
  syncArchiveDirectory(archiveRoot);
12664
12857
  verifyArchive(move);
12665
12858
  move.status = "archived";
@@ -12680,7 +12873,7 @@ function archiveNativeSkills(inventory, options) {
12680
12873
  recheckRootAliases(aliases);
12681
12874
  assertSafePath(entry.source);
12682
12875
  assertSafePath(entry.archive);
12683
- if (lstatSync8(entry.source, { throwIfNoEntry: false })) {
12876
+ if (lstatSync10(entry.source, { throwIfNoEntry: false })) {
12684
12877
  entry.status = "recovery-required";
12685
12878
  entry.conflict = "source-occupied";
12686
12879
  continue;
@@ -12694,10 +12887,10 @@ function archiveNativeSkills(inventory, options) {
12694
12887
  }
12695
12888
  if (entry.discoveryOnly) {
12696
12889
  linkSync(entry.archive, entry.source);
12697
- syncArchiveDirectory(dirname7(entry.source));
12890
+ syncArchiveDirectory(dirname9(entry.source));
12698
12891
  unlinkSync2(entry.archive);
12699
12892
  } else {
12700
- if (lstatSync8(entry.source, { throwIfNoEntry: false })) {
12893
+ if (lstatSync10(entry.source, { throwIfNoEntry: false })) {
12701
12894
  entry.status = "recovery-required";
12702
12895
  entry.conflict = "source-occupied";
12703
12896
  continue;
@@ -12705,7 +12898,7 @@ function archiveNativeSkills(inventory, options) {
12705
12898
  renameSync3(entry.archive, entry.source);
12706
12899
  }
12707
12900
  entry.status = "restored";
12708
- syncArchiveDirectory(dirname7(entry.source));
12901
+ syncArchiveDirectory(dirname9(entry.source));
12709
12902
  syncArchiveDirectory(archiveRoot);
12710
12903
  } catch {
12711
12904
  entry.status = "recovery-required";
@@ -12742,9 +12935,9 @@ function isSkillsOwnershipMarker(marker) {
12742
12935
  return typeof marker === "object" && marker !== null && Object.hasOwn(marker, "managedBy") && marker.managedBy === SYNC_MARKER_MANAGED_BY;
12743
12936
  }
12744
12937
  function hasSkillsOwnershipMarker(dir) {
12745
- const path = join16(dir, SYNC_MARKER_FILE);
12938
+ const path = join17(dir, SYNC_MARKER_FILE);
12746
12939
  try {
12747
- if (!lstatSync9(path).isFile())
12940
+ if (!lstatSync11(path).isFile())
12748
12941
  return false;
12749
12942
  const marker = JSON.parse(readFileSync12(path, "utf8"));
12750
12943
  return isSkillsOwnershipMarker(marker);
@@ -12766,9 +12959,9 @@ function resolveSyncAgents(arg) {
12766
12959
  function agentGlobalSkillsDir(agent, homeDir = homedir3()) {
12767
12960
  switch (agent) {
12768
12961
  case "opencode":
12769
- return join16(homeDir, ".config", "opencode", "skills");
12962
+ return join17(homeDir, ".config", "opencode", "skills");
12770
12963
  default:
12771
- return join16(homeDir, `.${agent}`, "skills");
12964
+ return join17(homeDir, `.${agent}`, "skills");
12772
12965
  }
12773
12966
  }
12774
12967
  function adaptSkillMdForAgent(skillMd, agent) {
@@ -12827,7 +13020,7 @@ function resolveSyncCorpus(options = {}) {
12827
13020
  function packageSourceRoots(source) {
12828
13021
  const roots = [];
12829
13022
  for (const sub of ["skills"]) {
12830
- const candidate = join16(source, sub);
13023
+ const candidate = join17(source, sub);
12831
13024
  if (existsSync11(candidate) && isDirectory(candidate))
12832
13025
  roots.push(candidate);
12833
13026
  }
@@ -12843,10 +13036,10 @@ function containsSkillDirectories(path) {
12843
13036
  return false;
12844
13037
  }
12845
13038
  return entries.some((entry) => {
12846
- const candidate = join16(path, entry);
13039
+ const candidate = join17(path, entry);
12847
13040
  if (!isDirectory(candidate))
12848
13041
  return false;
12849
- return existsSync11(join16(candidate, "SKILL.md")) || existsSync11(join16(candidate, "skill.json")) || existsSync11(join16(candidate, "package.json"));
13042
+ return existsSync11(join17(candidate, "SKILL.md")) || existsSync11(join17(candidate, "skill.json")) || existsSync11(join17(candidate, "package.json"));
12850
13043
  });
12851
13044
  }
12852
13045
  function isDirectory(path) {
@@ -12886,7 +13079,7 @@ function syncSkillsToAgents(options = {}) {
12886
13079
  actions.push({
12887
13080
  skill: name,
12888
13081
  agent,
12889
- path: join16(agentGlobalSkillsDir(agent, homeDir), name, "SKILL.md"),
13082
+ path: join17(agentGlobalSkillsDir(agent, homeDir), name, "SKILL.md"),
12890
13083
  action: "skip",
12891
13084
  reason: "not found in this machine's corpus"
12892
13085
  });
@@ -12916,7 +13109,7 @@ function syncSkillsToAgents(options = {}) {
12916
13109
  }
12917
13110
  function writeManagedAgentSkill(params) {
12918
13111
  const homeDir = params.homeDir ?? homedir3();
12919
- const dir = join16(agentGlobalSkillsDir(params.agent, homeDir), params.skill);
13112
+ const dir = join17(agentGlobalSkillsDir(params.agent, homeDir), params.skill);
12920
13113
  const result = writeManagedSkillDir(dir, params.skillMd, {
12921
13114
  skill: params.skill,
12922
13115
  source: params.source,
@@ -12933,8 +13126,8 @@ function writeManagedAgentSkill(params) {
12933
13126
  };
12934
13127
  }
12935
13128
  function writeManagedSkillDir(dir, skillMd, options) {
12936
- const skillMdPath = join16(dir, "SKILL.md");
12937
- const markerPath = join16(dir, SYNC_MARKER_FILE);
13129
+ const skillMdPath = join17(dir, "SKILL.md");
13130
+ const markerPath = join17(dir, SYNC_MARKER_FILE);
12938
13131
  const dirExists = existsSync11(dir);
12939
13132
  const managed = hasSkillsOwnershipMarker(dir);
12940
13133
  const hasSkillMd = existsSync11(skillMdPath);
@@ -12970,13 +13163,13 @@ function writeManagedSkillDir(dir, skillMd, options) {
12970
13163
  const action = dirExists ? "update" : "create";
12971
13164
  if (options.dryRun)
12972
13165
  return { action, path: skillMdPath };
12973
- const parentDir = dirname8(dir);
13166
+ const parentDir = dirname10(dir);
12974
13167
  mkdirSync6(parentDir, { recursive: true });
12975
- const transactionDir = mkdtempSync2(join16(parentDir, `.hasna-skills-write-${basename3(dir)}-`));
12976
- const candidateDir = join16(transactionDir, "candidate");
12977
- const backupDir = join16(transactionDir, "backup");
12978
- const candidateSkillMdPath = join16(candidateDir, "SKILL.md");
12979
- const candidateMarkerPath = join16(candidateDir, SYNC_MARKER_FILE);
13168
+ const transactionDir = mkdtempSync2(join17(parentDir, `.hasna-skills-write-${basename3(dir)}-`));
13169
+ const candidateDir = join17(transactionDir, "candidate");
13170
+ const backupDir = join17(transactionDir, "backup");
13171
+ const candidateSkillMdPath = join17(candidateDir, "SKILL.md");
13172
+ const candidateMarkerPath = join17(candidateDir, SYNC_MARKER_FILE);
12980
13173
  const marker = {
12981
13174
  managedBy: SYNC_MARKER_MANAGED_BY,
12982
13175
  skill: options.skill,
@@ -13025,7 +13218,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
13025
13218
  return { action, path: skillMdPath };
13026
13219
  }
13027
13220
  function removeManagedAgentSkill(skill, agent, homeDir = homedir3()) {
13028
- const dir = join16(agentGlobalSkillsDir(agent, homeDir), skill);
13221
+ const dir = join17(agentGlobalSkillsDir(agent, homeDir), skill);
13029
13222
  if (!hasSkillsOwnershipMarker(dir))
13030
13223
  return false;
13031
13224
  rmSync2(dir, { recursive: true, force: true });
@@ -13033,7 +13226,7 @@ function removeManagedAgentSkill(skill, agent, homeDir = homedir3()) {
13033
13226
  }
13034
13227
  function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = false) {
13035
13228
  if (kind === undefined || kind === "instruction" || preferBundledDocs) {
13036
- const skillMdPath = join16(skillPath, "SKILL.md");
13229
+ const skillMdPath = join17(skillPath, "SKILL.md");
13037
13230
  if (existsSync11(skillMdPath))
13038
13231
  return readFileSync12(skillMdPath, "utf-8");
13039
13232
  }
@@ -13066,7 +13259,7 @@ function normalizeSkillName(name) {
13066
13259
 
13067
13260
  // src/lib/project-state.ts
13068
13261
  import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
13069
- import { join as join17 } from "path";
13262
+ import { join as join18 } from "path";
13070
13263
  var VALID_PIN_SOURCES = [
13071
13264
  "official",
13072
13265
  "custom",
@@ -13081,10 +13274,10 @@ var SKILLS_PROJECT_DIR = ".skills";
13081
13274
  var PROJECT_CONFIG_FILE = "project.json";
13082
13275
  var DEFAULT_EXPORT_DIR = ".skills/exports";
13083
13276
  function getProjectStateDir(targetDir = process.cwd()) {
13084
- return join17(targetDir, SKILLS_PROJECT_DIR);
13277
+ return join18(targetDir, SKILLS_PROJECT_DIR);
13085
13278
  }
13086
13279
  function getProjectConfigPath(targetDir = process.cwd()) {
13087
- return join17(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
13280
+ return join18(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
13088
13281
  }
13089
13282
  function loadProjectConfig(targetDir = process.cwd()) {
13090
13283
  const path = getProjectConfigPath(targetDir);
@@ -13205,16 +13398,16 @@ function isPinSource(value) {
13205
13398
  }
13206
13399
 
13207
13400
  // src/lib/installer.ts
13208
- var __dirname2 = dirname9(fileURLToPath(import.meta.url));
13401
+ var __dirname2 = dirname11(fileURLToPath(import.meta.url));
13209
13402
  function findSkillsDir() {
13210
13403
  let dir = __dirname2;
13211
13404
  for (let i = 0;i < 5; i++) {
13212
- const candidate = join18(dir, "skills");
13405
+ const candidate = join19(dir, "skills");
13213
13406
  if (existsSync13(candidate) && !dir.includes(".skills"))
13214
13407
  return candidate;
13215
- dir = dirname9(dir);
13408
+ dir = dirname11(dir);
13216
13409
  }
13217
- return join18(__dirname2, "..", "skills");
13410
+ return join19(__dirname2, "..", "skills");
13218
13411
  }
13219
13412
  var SKILLS_DIR = findSkillsDir();
13220
13413
  function getSkillPath(name) {
@@ -13222,13 +13415,13 @@ function getSkillPath(name) {
13222
13415
  const portable = findPortableSkill(skillName);
13223
13416
  if (portable)
13224
13417
  return portable.path;
13225
- const legacyCustomPath = join18(getDataDir(), "custom", skillName);
13418
+ const legacyCustomPath = join19(getDataDir(), "custom", skillName);
13226
13419
  if (existsSync13(legacyCustomPath))
13227
13420
  return legacyCustomPath;
13228
13421
  const extensionPath = findExtensionSkillPath(skillName);
13229
13422
  if (extensionPath)
13230
13423
  return extensionPath;
13231
- return join18(SKILLS_DIR, skillName);
13424
+ return join19(SKILLS_DIR, skillName);
13232
13425
  }
13233
13426
  function getCanonicalSkillName(name) {
13234
13427
  return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
@@ -13291,7 +13484,7 @@ function createLocalSkillManifest(name, generateSkillMd) {
13291
13484
  if (!existsSync13(sourcePath))
13292
13485
  return null;
13293
13486
  let skillMd = "";
13294
- const skillMdPath = join18(sourcePath, "SKILL.md");
13487
+ const skillMdPath = join19(sourcePath, "SKILL.md");
13295
13488
  if (existsSync13(skillMdPath)) {
13296
13489
  skillMd = readFileSync14(skillMdPath, "utf-8");
13297
13490
  } else if (generateSkillMd) {
@@ -13365,16 +13558,16 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
13365
13558
  const base = projectDir || process.cwd();
13366
13559
  switch (agent) {
13367
13560
  case "pi":
13368
- return scope === "project" ? join18(base, ".pi", "skills") : join18(homedir4(), ".pi", "agent", "skills");
13561
+ return scope === "project" ? join19(base, ".pi", "skills") : join19(homedir4(), ".pi", "agent", "skills");
13369
13562
  case "opencode":
13370
- return scope === "project" ? join18(base, ".opencode", "skills") : join18(homedir4(), ".config", "opencode", "skills");
13563
+ return scope === "project" ? join19(base, ".opencode", "skills") : join19(homedir4(), ".config", "opencode", "skills");
13371
13564
  default:
13372
- return scope === "project" ? join18(base, `.${agent}`, "skills") : join18(homedir4(), `.${agent}`, "skills");
13565
+ return scope === "project" ? join19(base, `.${agent}`, "skills") : join19(homedir4(), `.${agent}`, "skills");
13373
13566
  }
13374
13567
  }
13375
13568
  function getAgentSkillPath(name, agent, scope = "global", projectDir) {
13376
13569
  const skillName = normalizeSkillName(getCanonicalSkillName(name));
13377
- return join18(getAgentSkillsDir(agent, scope, projectDir), skillName);
13570
+ return join19(getAgentSkillsDir(agent, scope, projectDir), skillName);
13378
13571
  }
13379
13572
  function installSkillForAgent(name, options, generateSkillMd) {
13380
13573
  assertNativeExportAllowed();
@@ -13410,7 +13603,7 @@ function removeSkillForAgent(name, options) {
13410
13603
  }
13411
13604
  function resolveAgentSkillMd(name, generateSkillMd) {
13412
13605
  const sourcePath = getSkillPath(name);
13413
- const skillMdPath = join18(sourcePath, "SKILL.md");
13606
+ const skillMdPath = join19(sourcePath, "SKILL.md");
13414
13607
  if (existsSync13(skillMdPath))
13415
13608
  return readFileSync14(skillMdPath, "utf-8");
13416
13609
  if (generateSkillMd)
@@ -13445,7 +13638,7 @@ function generateMinimalSkillMd(name) {
13445
13638
  "---",
13446
13639
  ""
13447
13640
  ].filter(Boolean);
13448
- const fallbackDoc = readFileIfExists(join18(sourcePath, "README.md")) || readFileIfExists(join18(sourcePath, "CLAUDE.md"));
13641
+ const fallbackDoc = readFileIfExists(join19(sourcePath, "README.md")) || readFileIfExists(join19(sourcePath, "CLAUDE.md"));
13449
13642
  if (fallbackDoc)
13450
13643
  return `${frontmatter.join(`
13451
13644
  `)}${fallbackDoc.trim()}
@@ -13464,7 +13657,7 @@ skills run ${canonicalName}
13464
13657
  `;
13465
13658
  }
13466
13659
  function readBundledSkillVersion(name) {
13467
- const pkgPath = join18(getSkillPath(name), "package.json");
13660
+ const pkgPath = join19(getSkillPath(name), "package.json");
13468
13661
  if (!existsSync13(pkgPath))
13469
13662
  return "unknown";
13470
13663
  try {
@@ -13481,21 +13674,21 @@ function loadProjectConfigCompat(targetDir) {
13481
13674
  return loadProjectConfig(targetDir);
13482
13675
  }
13483
13676
  // src/lib/run-state.ts
13484
- import { createHash as createHash7, randomBytes } from "crypto";
13677
+ import { createHash as createHash9, randomBytes } from "crypto";
13485
13678
  import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync15, readdirSync as readdirSync12, statSync as statSync10, writeFileSync as writeFileSync8 } from "fs";
13486
- import { extname, join as join19, relative as relative5 } from "path";
13679
+ import { extname, join as join20, relative as relative5 } from "path";
13487
13680
  function createSkillRun(params, targetDir = process.cwd()) {
13488
13681
  const now = new Date;
13489
13682
  const id = createRunId(now);
13490
13683
  const day = now.toISOString().slice(0, 10);
13491
13684
  const skillName = normalizeSkillName(params.skill);
13492
13685
  const root = getProjectStateDir(targetDir);
13493
- const runDir = join19(root, "runs", day, id);
13494
- const logsDir = join19(runDir, "logs");
13495
- const exportDir = join19(root, "exports", skillName, id);
13686
+ const runDir = join20(root, "runs", day, id);
13687
+ const logsDir = join20(runDir, "logs");
13688
+ const exportDir = join20(root, "exports", skillName, id);
13496
13689
  mkdirSync8(logsDir, { recursive: true });
13497
13690
  mkdirSync8(exportDir, { recursive: true });
13498
- mkdirSync8(join19(root, "tmp"), { recursive: true });
13691
+ mkdirSync8(join20(root, "tmp"), { recursive: true });
13499
13692
  const record2 = {
13500
13693
  id,
13501
13694
  skill: skillName,
@@ -13547,27 +13740,27 @@ function updateSkillRun(context, patch) {
13547
13740
  return context.record;
13548
13741
  }
13549
13742
  function writeRunLogs(context, stdout = "", stderr = "") {
13550
- writeFileSync8(join19(context.logsDir, "stdout.log"), stdout);
13551
- writeFileSync8(join19(context.logsDir, "stderr.log"), stderr);
13743
+ writeFileSync8(join20(context.logsDir, "stdout.log"), stdout);
13744
+ writeFileSync8(join20(context.logsDir, "stderr.log"), stderr);
13552
13745
  }
13553
13746
  function appendRunEvent(context, event, data = {}) {
13554
13747
  const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
13555
13748
  `;
13556
- const path = join19(context.runDir, "events.ndjson");
13749
+ const path = join20(context.runDir, "events.ndjson");
13557
13750
  const previous = existsSync14(path) ? readFileSync15(path, "utf-8") : "";
13558
13751
  writeFileSync8(path, previous + line);
13559
13752
  }
13560
13753
  function listSkillRuns(targetDir = process.cwd(), limit = 50) {
13561
- const runsRoot = join19(getProjectStateDir(targetDir), "runs");
13754
+ const runsRoot = join20(getProjectStateDir(targetDir), "runs");
13562
13755
  if (!existsSync14(runsRoot))
13563
13756
  return [];
13564
13757
  const records = [];
13565
13758
  for (const day of readdirSync12(runsRoot).sort().reverse()) {
13566
- const dayDir = join19(runsRoot, day);
13759
+ const dayDir = join20(runsRoot, day);
13567
13760
  if (!statSync10(dayDir).isDirectory())
13568
13761
  continue;
13569
13762
  for (const runId of readdirSync12(dayDir).sort().reverse()) {
13570
- const record2 = readRunRecord(join19(dayDir, runId));
13763
+ const record2 = readRunRecord(join20(dayDir, runId));
13571
13764
  if (record2)
13572
13765
  records.push(record2);
13573
13766
  if (records.length >= limit)
@@ -13577,25 +13770,25 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
13577
13770
  return records;
13578
13771
  }
13579
13772
  function findSkillRun(runId, targetDir = process.cwd()) {
13580
- const runsRoot = join19(getProjectStateDir(targetDir), "runs");
13773
+ const runsRoot = join20(getProjectStateDir(targetDir), "runs");
13581
13774
  if (!existsSync14(runsRoot))
13582
13775
  return null;
13583
13776
  for (const day of readdirSync12(runsRoot)) {
13584
- const record2 = readRunRecord(join19(runsRoot, day, runId));
13777
+ const record2 = readRunRecord(join20(runsRoot, day, runId));
13585
13778
  if (record2)
13586
13779
  return record2;
13587
13780
  }
13588
13781
  return null;
13589
13782
  }
13590
13783
  function getRunExportDir(runId, skill, targetDir = process.cwd()) {
13591
- return join19(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
13784
+ return join20(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
13592
13785
  }
13593
13786
  function writeRunRecord(context) {
13594
- writeFileSync8(join19(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
13787
+ writeFileSync8(join20(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
13595
13788
  `);
13596
13789
  }
13597
13790
  function writeArtifactsManifest(context, artifacts) {
13598
- writeFileSync8(join19(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
13791
+ writeFileSync8(join20(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
13599
13792
  `);
13600
13793
  }
13601
13794
  function collectRunArtifacts(context) {
@@ -13608,14 +13801,14 @@ function collectRunArtifacts(context) {
13608
13801
  artifacts.push({
13609
13802
  path: toProjectRelative(context.targetDir, path),
13610
13803
  mime: mimeForPath(path),
13611
- sha256: createHash7("sha256").update(bytes).digest("hex"),
13804
+ sha256: createHash9("sha256").update(bytes).digest("hex"),
13612
13805
  sizeBytes: stat.size
13613
13806
  });
13614
13807
  }
13615
13808
  return artifacts.sort((a, b) => a.path.localeCompare(b.path));
13616
13809
  }
13617
13810
  function readRunRecord(runDir) {
13618
- const path = join19(runDir, "run.json");
13811
+ const path = join20(runDir, "run.json");
13619
13812
  if (!existsSync14(path))
13620
13813
  return null;
13621
13814
  try {
@@ -13627,7 +13820,7 @@ function readRunRecord(runDir) {
13627
13820
  function walkFiles(dir) {
13628
13821
  const files = [];
13629
13822
  for (const entry of readdirSync12(dir)) {
13630
- const full = join19(dir, entry);
13823
+ const full = join20(dir, entry);
13631
13824
  if (statSync10(full).isDirectory())
13632
13825
  files.push(...walkFiles(full));
13633
13826
  else
@@ -13673,8 +13866,8 @@ function mimeForPath(path) {
13673
13866
  }
13674
13867
  }
13675
13868
  // src/lib/dependency-preparation.ts
13676
- import { constants as constants4, closeSync as closeSync4, fstatSync as fstatSync4, lstatSync as lstatSync10, mkdirSync as mkdirSync9, openSync as openSync4, readFileSync as readFileSync16, readdirSync as readdirSync13, rmdirSync as rmdirSync2, unlinkSync as unlinkSync3, ftruncateSync, writeSync } from "fs";
13677
- import { join as join20 } from "path";
13869
+ import { constants as constants5, closeSync as closeSync5, fstatSync as fstatSync5, lstatSync as lstatSync12, mkdirSync as mkdirSync9, openSync as openSync5, readFileSync as readFileSync16, readdirSync as readdirSync13, rmdirSync as rmdirSync2, unlinkSync as unlinkSync3, ftruncateSync, writeSync } from "fs";
13870
+ import { join as join21 } from "path";
13678
13871
 
13679
13872
  // src/lib/preparation-process.ts
13680
13873
  function capturePreparationProcess(child, platform = process.platform, signalGroup = (pid, signal) => process.kill(pid, signal)) {
@@ -13721,15 +13914,15 @@ var DEPENDENCY_PREPARATION_MARKER = ".skills-dependency-preparation";
13721
13914
  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.";
13722
13915
  function hasDependencyPreparationMarker(skillPath) {
13723
13916
  try {
13724
- lstatSync10(join20(skillPath, DEPENDENCY_PREPARATION_MARKER));
13917
+ lstatSync12(join21(skillPath, DEPENDENCY_PREPARATION_MARKER));
13725
13918
  return true;
13726
13919
  } catch (error) {
13727
13920
  return error.code !== "ENOENT";
13728
13921
  }
13729
13922
  }
13730
13923
  function claimPreparation(skillPath) {
13731
- const marker = join20(skillPath, DEPENDENCY_PREPARATION_MARKER);
13732
- const state = join20(marker, "state.json"), active = join20(marker, "active");
13924
+ const marker = join21(skillPath, DEPENDENCY_PREPARATION_MARKER);
13925
+ const state = join21(marker, "state.json"), active = join21(marker, "active");
13733
13926
  let created = false;
13734
13927
  try {
13735
13928
  mkdirSync9(marker, { mode: 448 });
@@ -13740,21 +13933,21 @@ function claimPreparation(skillPath) {
13740
13933
  }
13741
13934
  let fd;
13742
13935
  try {
13743
- if (!lstatSync10(marker).isDirectory() || lstatSync10(marker).isSymbolicLink())
13936
+ if (!lstatSync12(marker).isDirectory() || lstatSync12(marker).isSymbolicLink())
13744
13937
  return;
13745
13938
  if (!created) {
13746
13939
  if (readdirSync13(marker).some((name) => name !== "state.json"))
13747
13940
  return;
13748
- if (!lstatSync10(state).isFile() || lstatSync10(state).isSymbolicLink() || lstatSync10(state).size > 1024)
13941
+ if (!lstatSync12(state).isFile() || lstatSync12(state).isSymbolicLink() || lstatSync12(state).size > 1024)
13749
13942
  return;
13750
13943
  const previous = JSON.parse(readFileSync16(state, "utf8"));
13751
13944
  if (previous.version !== 1 || previous.status !== "failed" || Object.keys(previous).length !== 2)
13752
13945
  return;
13753
13946
  }
13754
13947
  mkdirSync9(active, { mode: 448 });
13755
- fd = openSync4(state, constants4.O_RDWR | constants4.O_NOFOLLOW | (created ? constants4.O_CREAT | constants4.O_EXCL : 0), 384);
13756
- if (!fstatSync4(fd).isFile()) {
13757
- closeSync4(fd);
13948
+ fd = openSync5(state, constants5.O_RDWR | constants5.O_NOFOLLOW | (created ? constants5.O_CREAT | constants5.O_EXCL : 0), 384);
13949
+ if (!fstatSync5(fd).isFile()) {
13950
+ closeSync5(fd);
13758
13951
  return;
13759
13952
  }
13760
13953
  const descriptor = fd;
@@ -13766,7 +13959,7 @@ function claimPreparation(skillPath) {
13766
13959
  record2("pending");
13767
13960
  return { abandon() {
13768
13961
  try {
13769
- closeSync4(descriptor);
13962
+ closeSync5(descriptor);
13770
13963
  } catch {}
13771
13964
  }, finish(success) {
13772
13965
  try {
@@ -13774,7 +13967,7 @@ function claimPreparation(skillPath) {
13774
13967
  unlinkSync3(state);
13775
13968
  else
13776
13969
  record2("failed");
13777
- closeSync4(descriptor);
13970
+ closeSync5(descriptor);
13778
13971
  fd = undefined;
13779
13972
  rmdirSync2(active);
13780
13973
  if (success)
@@ -13782,7 +13975,7 @@ function claimPreparation(skillPath) {
13782
13975
  return true;
13783
13976
  } catch {
13784
13977
  try {
13785
- closeSync4(descriptor);
13978
+ closeSync5(descriptor);
13786
13979
  } catch {}
13787
13980
  return false;
13788
13981
  }
@@ -13790,7 +13983,7 @@ function claimPreparation(skillPath) {
13790
13983
  } catch {
13791
13984
  if (fd !== undefined)
13792
13985
  try {
13793
- closeSync4(fd);
13986
+ closeSync5(fd);
13794
13987
  } catch {}
13795
13988
  return;
13796
13989
  }
@@ -13881,11 +14074,11 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
13881
14074
 
13882
14075
  // src/lib/skillinfo.ts
13883
14076
  import { existsSync as existsSync15, readFileSync as readFileSync17 } from "fs";
13884
- import { join as join21 } from "path";
14077
+ import { join as join22 } from "path";
13885
14078
  function isInstructionSkillDir(skillPath, meta) {
13886
14079
  if (meta?.kind === "instruction")
13887
14080
  return true;
13888
- const skillMdPath = join21(skillPath, "SKILL.md");
14081
+ const skillMdPath = join22(skillPath, "SKILL.md");
13889
14082
  if (!existsSync15(skillMdPath))
13890
14083
  return false;
13891
14084
  try {
@@ -13915,9 +14108,9 @@ function getSkillDocs(name) {
13915
14108
  if (!existsSync15(skillPath))
13916
14109
  return null;
13917
14110
  return {
13918
- skillMd: readIfExists(join21(skillPath, "SKILL.md")),
13919
- readme: readIfExists(join21(skillPath, "README.md")),
13920
- claudeMd: readIfExists(join21(skillPath, "CLAUDE.md"))
14111
+ skillMd: readIfExists(join22(skillPath, "SKILL.md")),
14112
+ readme: readIfExists(join22(skillPath, "README.md")),
14113
+ claudeMd: readIfExists(join22(skillPath, "CLAUDE.md"))
13921
14114
  };
13922
14115
  }
13923
14116
  function getSkillBestDoc(name) {
@@ -13932,14 +14125,14 @@ function getSkillRequirements(name) {
13932
14125
  return null;
13933
14126
  const texts = [];
13934
14127
  for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
13935
- const content = readIfExists(join21(skillPath, file));
14128
+ const content = readIfExists(join22(skillPath, file));
13936
14129
  if (content)
13937
14130
  texts.push(content);
13938
14131
  }
13939
14132
  const meta = getSkill(name);
13940
14133
  let dependencies = {};
13941
14134
  try {
13942
- dependencies = JSON.parse(readFileSync17(join21(skillPath, "package.json"), "utf8")).dependencies || {};
14135
+ dependencies = JSON.parse(readFileSync17(join22(skillPath, "package.json"), "utf8")).dependencies || {};
13943
14136
  } catch {}
13944
14137
  return getSkillRequirementsFromContent(name, texts, dependencies, meta);
13945
14138
  }
@@ -14001,7 +14194,7 @@ async function runSkill(name, args, options = {}) {
14001
14194
  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'.`
14002
14195
  };
14003
14196
  }
14004
- const pkgPath = join21(skillPath, "package.json");
14197
+ const pkgPath = join22(skillPath, "package.json");
14005
14198
  if (!existsSync15(pkgPath)) {
14006
14199
  return { exitCode: 1, error: `No package.json in skill '${name}'` };
14007
14200
  }
@@ -14021,12 +14214,12 @@ async function runSkill(name, args, options = {}) {
14021
14214
  } catch {
14022
14215
  return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
14023
14216
  }
14024
- const entryPath = join21(skillPath, entryPoint);
14217
+ const entryPath = join22(skillPath, entryPoint);
14025
14218
  if (!existsSync15(entryPath)) {
14026
14219
  return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
14027
14220
  }
14028
14221
  const env = { ...process.env, ...options.env };
14029
- if (!existsSync15(join21(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
14222
+ if (!existsSync15(join22(skillPath, "node_modules")) || hasDependencyPreparationMarker(skillPath)) {
14030
14223
  const failure = await prepareSkillDependencies(skillPath, env, options.preparationTimeoutMs);
14031
14224
  if (failure)
14032
14225
  return failure;
@@ -14104,10 +14297,10 @@ function generateSkillMd(name) {
14104
14297
  "---"
14105
14298
  ].join(`
14106
14299
  `);
14107
- const readme = readIfExists(join21(skillPath, "README.md"));
14108
- const claudeMd = readIfExists(join21(skillPath, "CLAUDE.md"));
14300
+ const readme = readIfExists(join22(skillPath, "README.md"));
14301
+ const claudeMd = readIfExists(join22(skillPath, "CLAUDE.md"));
14109
14302
  let cliCommand = null;
14110
- const pkgPath = join21(skillPath, "package.json");
14303
+ const pkgPath = join22(skillPath, "package.json");
14111
14304
  if (existsSync15(pkgPath)) {
14112
14305
  try {
14113
14306
  const pkg = JSON.parse(readFileSync17(pkgPath, "utf-8"));
@@ -18167,11 +18360,11 @@ var NEVER = INVALID;
18167
18360
  // ../contracts/dist/client/transport.js
18168
18361
  import { isIP } from "net";
18169
18362
  import { spawnSync } from "child_process";
18170
- import { closeSync as closeSync5, fstatSync as fstatSync5, openSync as openSync5, readFileSync as readFileSync18 } from "fs";
18363
+ import { closeSync as closeSync6, fstatSync as fstatSync6, openSync as openSync6, readFileSync as readFileSync18 } from "fs";
18171
18364
  import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
18172
18365
  import { createRequire } from "module";
18173
18366
  import { hostname as osHostname } from "os";
18174
- import { isAbsolute as isAbsolute6, join as join22 } from "path";
18367
+ import { isAbsolute as isAbsolute8, join as join23 } from "path";
18175
18368
  function envToken(name) {
18176
18369
  return name.toUpperCase().replace(/-/g, "_");
18177
18370
  }
@@ -18230,21 +18423,21 @@ function homeDir(env) {
18230
18423
  }
18231
18424
  function absoluteOverride(env, key) {
18232
18425
  const value = env[key]?.trim();
18233
- return value && isAbsolute6(value) ? value : null;
18426
+ return value && isAbsolute8(value) ? value : null;
18234
18427
  }
18235
18428
  function hasnaHomeDir(env) {
18236
18429
  const override = absoluteOverride(env, HASNA_HOME_ENV_KEY);
18237
18430
  if (override)
18238
18431
  return override;
18239
18432
  const home = homeDir(env);
18240
- return home ? join22(home, HASNA_HOME_DIR) : null;
18433
+ return home ? join23(home, HASNA_HOME_DIR) : null;
18241
18434
  }
18242
18435
  function appConfigDir(name, env) {
18243
18436
  const configRoot = absoluteOverride(env, HASNA_CONFIG_HOME_ENV_KEY);
18244
18437
  if (configRoot)
18245
- return join22(configRoot, name);
18438
+ return join23(configRoot, name);
18246
18439
  const root = hasnaHomeDir(env);
18247
- return root ? join22(root, name, CONFIG_SUBDIR) : null;
18440
+ return root ? join23(root, name, CONFIG_SUBDIR) : null;
18248
18441
  }
18249
18442
  function credentialDiskSourceList(name, env, profile = null) {
18250
18443
  if (!SAFE_APP_SLUG.test(name))
@@ -18253,7 +18446,7 @@ function credentialDiskSourceList(name, env, profile = null) {
18253
18446
  if (!directory)
18254
18447
  return [];
18255
18448
  const file = profile ? `${CREDENTIALS_FILE}-${profile}` : CREDENTIALS_FILE;
18256
- return [{ path: join22(directory, file), tier: "disk" }];
18449
+ return [{ path: join23(directory, file), tier: "disk" }];
18257
18450
  }
18258
18451
  function credentialDiskSources(name, env) {
18259
18452
  return credentialDiskSourceList(name, env, null).map((s) => s.path);
@@ -18307,7 +18500,7 @@ function readAppConfigFile(path) {
18307
18500
  };
18308
18501
  let fd = -1;
18309
18502
  try {
18310
- fd = openSync5(path, O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
18503
+ fd = openSync6(path, O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
18311
18504
  } catch (error) {
18312
18505
  const code = error.code;
18313
18506
  if (code === "ENOENT" || code === "ENOTDIR")
@@ -18317,7 +18510,7 @@ function readAppConfigFile(path) {
18317
18510
  unsafe(`the path could not be opened (${code ?? "unknown error"})`);
18318
18511
  }
18319
18512
  try {
18320
- const before = fstatSync5(fd);
18513
+ const before = fstatSync6(fd);
18321
18514
  if (!before.isFile())
18322
18515
  unsafe("the path is not a regular file");
18323
18516
  if (!configFileModeAllowed(before.mode)) {
@@ -18329,14 +18522,14 @@ function readAppConfigFile(path) {
18329
18522
  if (before.size > MAX_CREDENTIAL_FILE_BYTES)
18330
18523
  unsafe("the file exceeds the size limit");
18331
18524
  const bytes = readFileSync18(fd);
18332
- const after = fstatSync5(fd);
18525
+ const after = fstatSync6(fd);
18333
18526
  if (!configFileReadsCoherent(before, after)) {
18334
18527
  unsafe("the file changed while being read");
18335
18528
  }
18336
18529
  return parseEnvFile(bytes.toString("utf8"));
18337
18530
  } finally {
18338
18531
  if (fd !== -1)
18339
- closeSync5(fd);
18532
+ closeSync6(fd);
18340
18533
  }
18341
18534
  }
18342
18535
  function readCredentialFile(path, apiKeyKeys) {
@@ -18851,7 +19044,7 @@ var AUTHORITY_OVERRIDE_HEADERS = new Set([
18851
19044
  ]);
18852
19045
 
18853
19046
  // src/lib/instance-credentials.ts
18854
- import { closeSync as closeSync6, constants as constants5, fstatSync as fstatSync6, lstatSync as lstatSync11, openSync as openSync6, readSync as readSync4 } from "fs";
19047
+ import { closeSync as closeSync7, constants as constants6, fstatSync as fstatSync7, lstatSync as lstatSync13, openSync as openSync7, readSync as readSync5 } from "fs";
18855
19048
  var SKILLS_BOUND_API_URL = "HASNA_SKILLS_BOUND_API_URL";
18856
19049
  function selectedSkillsProfile(env, explicit) {
18857
19050
  const selected = explicit ?? env.HASNA_PROFILE;
@@ -18867,7 +19060,7 @@ function skillsProfileCredentialFiles(env, explicit) {
18867
19060
  }
18868
19061
  function fileIdentity(file) {
18869
19062
  try {
18870
- return lstatSync11(file);
19063
+ return lstatSync13(file);
18871
19064
  } catch (error) {
18872
19065
  if (["ENOENT", "ENOTDIR"].includes(error.code ?? ""))
18873
19066
  return null;
@@ -18888,14 +19081,14 @@ function captureSkillsCredentialFiles(files) {
18888
19081
  function readMetadataText(file) {
18889
19082
  let fd;
18890
19083
  try {
18891
- fd = openSync6(file, constants5.O_RDONLY | constants5.O_NOFOLLOW | constants5.O_NONBLOCK);
19084
+ fd = openSync7(file, constants6.O_RDONLY | constants6.O_NOFOLLOW | constants6.O_NONBLOCK);
18892
19085
  } catch (error) {
18893
19086
  if (["ENOENT", "ENOTDIR"].includes(error.code ?? ""))
18894
19087
  return null;
18895
19088
  throw new Error("Cannot safely read Skills instance configuration");
18896
19089
  }
18897
19090
  try {
18898
- const before = fstatSync6(fd);
19091
+ const before = fstatSync7(fd);
18899
19092
  const uid = process.getuid?.() ?? process.geteuid?.();
18900
19093
  if (!before.isFile() || ![256, 384].includes(before.mode & 4095) || uid !== undefined && before.uid !== uid || before.size > 64 * 1024) {
18901
19094
  throw new Error("Unsafe Skills instance configuration; expected a bounded owner-only regular file");
@@ -18903,17 +19096,17 @@ function readMetadataText(file) {
18903
19096
  const bytes = Buffer.alloc(64 * 1024 + 1);
18904
19097
  let length = 0;
18905
19098
  while (length < bytes.length) {
18906
- const count = readSync4(fd, bytes, length, bytes.length - length, null);
19099
+ const count = readSync5(fd, bytes, length, bytes.length - length, null);
18907
19100
  if (!count)
18908
19101
  break;
18909
19102
  length += count;
18910
19103
  }
18911
- if (length > 64 * 1024 || !unchanged(before, fstatSync6(fd)) || !unchanged(before, fileIdentity(file))) {
19104
+ if (length > 64 * 1024 || !unchanged(before, fstatSync7(fd)) || !unchanged(before, fileIdentity(file))) {
18912
19105
  throw new Error("Skills instance configuration changed while reading");
18913
19106
  }
18914
19107
  return bytes.subarray(0, length).toString("utf8");
18915
19108
  } finally {
18916
- closeSync6(fd);
19109
+ closeSync7(fd);
18917
19110
  }
18918
19111
  }
18919
19112
  function readSkillsInstanceMetadata(file) {
@@ -20547,7 +20740,7 @@ function object3(value) {
20547
20740
  }
20548
20741
 
20549
20742
  // src/lib/remote-files.ts
20550
- import { createHash as createHash8 } from "crypto";
20743
+ import { createHash as createHash10 } from "crypto";
20551
20744
  var MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
20552
20745
  function describeRemoteFiles(files) {
20553
20746
  if (files.length > 10)
@@ -20565,7 +20758,7 @@ function describeRemoteFiles(files) {
20565
20758
  });
20566
20759
  }
20567
20760
  function sha256(bytes) {
20568
- return createHash8("sha256").update(bytes).digest("hex");
20761
+ return createHash10("sha256").update(bytes).digest("hex");
20569
20762
  }
20570
20763
  async function readBoundedResponse(response, maximum) {
20571
20764
  if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
@@ -20658,10 +20851,10 @@ async function readQuoteUnavailableCode(response) {
20658
20851
  let timer;
20659
20852
  let deadlineExceeded = false;
20660
20853
  const expired = Symbol("quote body deadline");
20661
- const deadline = new Promise((resolve7) => {
20854
+ const deadline = new Promise((resolve9) => {
20662
20855
  timer = setTimeout(() => {
20663
20856
  deadlineExceeded = true;
20664
- resolve7(expired);
20857
+ resolve9(expired);
20665
20858
  reader.cancel().catch(() => {});
20666
20859
  }, 1500);
20667
20860
  });
@@ -21476,9 +21669,9 @@ async function createRemoteSkillsClient(env = process.env) {
21476
21669
  }
21477
21670
  // src/lib/scheduler.ts
21478
21671
  import { existsSync as existsSync16, readFileSync as readFileSync19, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10 } from "fs";
21479
- import { join as join23 } from "path";
21672
+ import { join as join24 } from "path";
21480
21673
  function getSchedulesPath(targetDir = process.cwd()) {
21481
- return join23(targetDir, ".skills", "schedules.json");
21674
+ return join24(targetDir, ".skills", "schedules.json");
21482
21675
  }
21483
21676
  function loadSchedules(targetDir = process.cwd()) {
21484
21677
  const path = getSchedulesPath(targetDir);
@@ -21491,7 +21684,7 @@ function loadSchedules(targetDir = process.cwd()) {
21491
21684
  }
21492
21685
  function saveSchedules(data, targetDir = process.cwd()) {
21493
21686
  const path = getSchedulesPath(targetDir);
21494
- const dir = join23(targetDir, ".skills");
21687
+ const dir = join24(targetDir, ".skills");
21495
21688
  if (!existsSync16(dir))
21496
21689
  mkdirSync10(dir, { recursive: true });
21497
21690
  writeFileSync9(path, JSON.stringify(data, null, 2));
@@ -21679,7 +21872,7 @@ function recordScheduleRun(id, status, targetDir) {
21679
21872
  saveSchedules(data, targetDir);
21680
21873
  }
21681
21874
  // src/lib/revision.ts
21682
- import { createHash as createHash9 } from "crypto";
21875
+ import { createHash as createHash11 } from "crypto";
21683
21876
  var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
21684
21877
  function revisionIdOf(content) {
21685
21878
  const canonical = JSON.stringify({
@@ -21695,14 +21888,14 @@ function revisionIdOf(content) {
21695
21888
  bundleSha256: content.bundleSha256 ?? null,
21696
21889
  bundleByteSize: content.bundleByteSize ?? null
21697
21890
  });
21698
- return createHash9("sha256").update(canonical).digest("hex");
21891
+ return createHash11("sha256").update(canonical).digest("hex");
21699
21892
  }
21700
21893
  function revisionIdOfRecord(record5) {
21701
21894
  return revisionIdOf(record5);
21702
21895
  }
21703
21896
  // src/lib/pull.ts
21704
21897
  import { existsSync as existsSync17, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync3, readFileSync as readFileSync20, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "fs";
21705
- import { dirname as dirname10, join as join24 } from "path";
21898
+ import { dirname as dirname12, join as join25 } from "path";
21706
21899
 
21707
21900
  // src/lib/skill-bundles.ts
21708
21901
  import { createHmac, timingSafeEqual } from "crypto";
@@ -21796,7 +21989,7 @@ async function pullOne(client, rawName, corpusOptions, verify) {
21796
21989
  return reconcileTombstone(slug, corpusOptions);
21797
21990
  }
21798
21991
  if (bundleResponse.status === 404) {
21799
- const marker = readPullMarker(join24(getPortableSkillsRoot(corpusOptions), slug));
21992
+ const marker = readPullMarker(join25(getPortableSkillsRoot(corpusOptions), slug));
21800
21993
  if (isPublishedInstallMarker(marker)) {
21801
21994
  return { name: slug, success: true, purged: true, removed: false };
21802
21995
  }
@@ -21835,7 +22028,7 @@ async function pullOne(client, rawName, corpusOptions, verify) {
21835
22028
  return { name: slug, success: false, error: `Skill '${slug}' was not found on the configured Skills instance.` };
21836
22029
  }
21837
22030
  if (!meta?.revisionId) {
21838
- const marker = readPullMarker(join24(getPortableSkillsRoot(corpusOptions), slug));
22031
+ const marker = readPullMarker(join25(getPortableSkillsRoot(corpusOptions), slug));
21839
22032
  if (isPublishedInstallMarker(marker)) {
21840
22033
  return { name: slug, success: true, purged: true, removed: false };
21841
22034
  }
@@ -21892,7 +22085,7 @@ function provenRevision(meta, slug, bundle) {
21892
22085
  return declared;
21893
22086
  }
21894
22087
  function reconcileTombstone(slug, corpusOptions) {
21895
- const target = join24(getPortableSkillsRoot(corpusOptions), slug);
22088
+ const target = join25(getPortableSkillsRoot(corpusOptions), slug);
21896
22089
  if (!hasSkillsOwnershipMarker(target)) {
21897
22090
  return { name: slug, success: true, tombstoned: true, removed: false, leftInPlace: true };
21898
22091
  }
@@ -21901,7 +22094,7 @@ function reconcileTombstone(slug, corpusOptions) {
21901
22094
  }
21902
22095
  function readPullMarker(dir) {
21903
22096
  try {
21904
- return JSON.parse(readFileSync20(join24(dir, PULL_MARKER_FILE), "utf-8"));
22097
+ return JSON.parse(readFileSync20(join25(dir, PULL_MARKER_FILE), "utf-8"));
21905
22098
  } catch {
21906
22099
  return null;
21907
22100
  }
@@ -22036,15 +22229,15 @@ function verifyBundleResponseBytes(buffer, response, verify = {}) {
22036
22229
  function installBundleAtomically(name, entries, options = {}, marker = {}) {
22037
22230
  const root = getPortableSkillsRoot(options);
22038
22231
  mkdirSync11(root, { recursive: true });
22039
- const target = join24(root, name);
22232
+ const target = join25(root, name);
22040
22233
  const created = !existsSync17(target);
22041
- const staging = mkdtempSync3(join24(root, `.pull-${name}-`));
22234
+ const staging = mkdtempSync3(join25(root, `.pull-${name}-`));
22042
22235
  let moved = false;
22043
22236
  let backup = null;
22044
22237
  try {
22045
22238
  for (const entry of entries) {
22046
- const destination = join24(staging, entry.path);
22047
- mkdirSync11(dirname10(destination), { recursive: true });
22239
+ const destination = join25(staging, entry.path);
22240
+ mkdirSync11(dirname12(destination), { recursive: true });
22048
22241
  writeFileSync10(destination, entry.bytes, { mode: entry.mode });
22049
22242
  }
22050
22243
  writePullMarker(staging, {
@@ -22056,8 +22249,8 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
22056
22249
  ...marker.revisionId ? { revisionId: marker.revisionId } : {}
22057
22250
  });
22058
22251
  if (existsSync17(target)) {
22059
- backup = mkdtempSync3(join24(root, `.pull-backup-${name}-`));
22060
- renameSync5(target, join24(backup, name));
22252
+ backup = mkdtempSync3(join25(root, `.pull-backup-${name}-`));
22253
+ renameSync5(target, join25(backup, name));
22061
22254
  moved = true;
22062
22255
  }
22063
22256
  renameSync5(staging, target);
@@ -22065,9 +22258,9 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
22065
22258
  rmSync4(backup, { recursive: true, force: true });
22066
22259
  } catch (error) {
22067
22260
  rmSync4(staging, { recursive: true, force: true });
22068
- if (moved && backup && existsSync17(join24(backup, name))) {
22261
+ if (moved && backup && existsSync17(join25(backup, name))) {
22069
22262
  try {
22070
- renameSync5(join24(backup, name), target);
22263
+ renameSync5(join25(backup, name), target);
22071
22264
  } catch {}
22072
22265
  }
22073
22266
  throw error;
@@ -22086,7 +22279,7 @@ function writePullMarker(dir, record5) {
22086
22279
  ...record5.revisionId ? { revisionId: record5.revisionId } : {},
22087
22280
  syncedAt: new Date().toISOString()
22088
22281
  };
22089
- writeFileSync10(join24(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
22282
+ writeFileSync10(join25(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
22090
22283
  `);
22091
22284
  }
22092
22285
  async function safeMeta(client, slug) {
@@ -22242,11 +22435,11 @@ function findSkillsParityForMcpTool(tool) {
22242
22435
  }
22243
22436
  // src/lib/registry-sync.ts
22244
22437
  import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync11 } from "fs";
22245
- import { dirname as dirname11, relative as relative6 } from "path";
22438
+ import { dirname as dirname13, relative as relative6 } from "path";
22246
22439
  // package.json
22247
22440
  var package_default = {
22248
22441
  name: "@hasna/skills",
22249
- version: "0.7.1",
22442
+ version: "0.7.2",
22250
22443
  description: "Skills library for AI coding agents",
22251
22444
  type: "module",
22252
22445
  bin: {
@@ -22428,7 +22621,7 @@ function createRegistrySyncArtifact(options = {}) {
22428
22621
  };
22429
22622
  }
22430
22623
  function writeRegistrySyncArtifact(path, artifact) {
22431
- mkdirSync12(dirname11(path), { recursive: true });
22624
+ mkdirSync12(dirname13(path), { recursive: true });
22432
22625
  writeFileSync11(path, `${JSON.stringify(artifact, null, 2)}
22433
22626
  `);
22434
22627
  }
@@ -23502,14 +23695,14 @@ function clone2(value) {
23502
23695
  }
23503
23696
  // src/lib/feedback.ts
23504
23697
  import { appendFileSync, existsSync as existsSync18, mkdirSync as mkdirSync13 } from "fs";
23505
- import { dirname as dirname12, join as join25 } from "path";
23698
+ import { dirname as dirname14, join as join26 } from "path";
23506
23699
  import { Database } from "bun:sqlite";
23507
23700
  function getFeedbackDbPath() {
23508
- return join25(getDataDir(), "skills.db");
23701
+ return join26(getDataDir(), "skills.db");
23509
23702
  }
23510
23703
  function getFeedbackDb() {
23511
23704
  const dbPath = getFeedbackDbPath();
23512
- const dir = dirname12(dbPath);
23705
+ const dir = dirname14(dbPath);
23513
23706
  if (!existsSync18(dir))
23514
23707
  mkdirSync13(dir, { recursive: true });
23515
23708
  const db = new Database(dbPath);
@@ -23537,8 +23730,8 @@ function saveFeedback(input) {
23537
23730
  throw new Error("Feedback message is required");
23538
23731
  const category = input.category ?? "general";
23539
23732
  if (isApiMode()) {
23540
- const path = join25(getDataDir(), "feedback.jsonl");
23541
- const dir = dirname12(path);
23733
+ const path = join26(getDataDir(), "feedback.jsonl");
23734
+ const dir = dirname14(path);
23542
23735
  if (!existsSync18(dir))
23543
23736
  mkdirSync13(dir, { recursive: true });
23544
23737
  appendFileSync(path, JSON.stringify({ message, category, email: input.email ?? null, agent: input.agent ?? null, version: input.version ?? null, createdAt: new Date().toISOString() }) + `
@@ -23561,7 +23754,7 @@ function isApiMode(env = process.env) {
23561
23754
  }
23562
23755
  }
23563
23756
  // src/lib/native-storage.ts
23564
- import { createHash as createHash10, createHmac as createHmac2 } from "crypto";
23757
+ import { createHash as createHash12, createHmac as createHmac2 } from "crypto";
23565
23758
  import {
23566
23759
  existsSync as existsSync19,
23567
23760
  mkdirSync as mkdirSync14,
@@ -23570,7 +23763,7 @@ import {
23570
23763
  statSync as statSync11,
23571
23764
  writeFileSync as writeFileSync12
23572
23765
  } from "fs";
23573
- import { dirname as dirname13, join as join26, normalize as normalize3, relative as relative7, sep as sep5 } from "path";
23766
+ import { dirname as dirname15, join as join27, normalize as normalize3, relative as relative7, sep as sep5 } from "path";
23574
23767
  var SKILLS_STORAGE_TABLES = [
23575
23768
  "skills_sync_records",
23576
23769
  "skills_sync_cursors"
@@ -23707,7 +23900,7 @@ function getSkillsNativeStorageStatus(options = {}) {
23707
23900
  local: {
23708
23901
  dataDir: getDataDir(),
23709
23902
  projectStateDir: getProjectStateDir(targetDir),
23710
- feedbackDbPath: join26(getDataDir(), "skills.db")
23903
+ feedbackDbPath: join27(getDataDir(), "skills.db")
23711
23904
  },
23712
23905
  remote: {
23713
23906
  databaseConfigured: Boolean(config.databaseUrl),
@@ -23737,7 +23930,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
23737
23930
  files.push({
23738
23931
  path: relativePath,
23739
23932
  sizeBytes: bytes.byteLength,
23740
- sha256: createHash10("sha256").update(bytes).digest("hex"),
23933
+ sha256: createHash12("sha256").update(bytes).digest("hex"),
23741
23934
  ...options.includeFileContents ? { contentBase64: Buffer.from(bytes).toString("base64") } : {}
23742
23935
  });
23743
23936
  }
@@ -23762,11 +23955,11 @@ function importSkillsLocalSnapshot(snapshot, targetDir = process.cwd(), options
23762
23955
  continue;
23763
23956
  }
23764
23957
  const bytes = Buffer.from(file.contentBase64, "base64");
23765
- const hash2 = createHash10("sha256").update(bytes).digest("hex");
23958
+ const hash2 = createHash12("sha256").update(bytes).digest("hex");
23766
23959
  if (hash2 !== file.sha256) {
23767
23960
  throw new Error(`Snapshot file checksum mismatch: ${file.path}`);
23768
23961
  }
23769
- mkdirSync14(dirname13(absolutePath), { recursive: true });
23962
+ mkdirSync14(dirname15(absolutePath), { recursive: true });
23770
23963
  writeFileSync12(absolutePath, bytes);
23771
23964
  written += 1;
23772
23965
  }
@@ -24064,7 +24257,7 @@ function parsePositiveInteger(value) {
24064
24257
  function walkFiles2(dir) {
24065
24258
  const files = [];
24066
24259
  for (const entry of readdirSync14(dir)) {
24067
- const full = join26(dir, entry);
24260
+ const full = join27(dir, entry);
24068
24261
  const stats = statSync11(full);
24069
24262
  if (stats.isDirectory())
24070
24263
  files.push(...walkFiles2(full));
@@ -24081,7 +24274,7 @@ function resolveSnapshotPath(targetDir, snapshotPath) {
24081
24274
  if (!toPosix(normalizedPath).startsWith(".skills/")) {
24082
24275
  throw new Error(`Snapshot path must stay inside .skills: ${snapshotPath}`);
24083
24276
  }
24084
- return join26(targetDir, normalizedPath);
24277
+ return join27(targetDir, normalizedPath);
24085
24278
  }
24086
24279
  function normalizeS3Prefix(prefix) {
24087
24280
  return (prefix ?? "").trim().replace(/^\/+|\/+$/g, "");
@@ -24094,7 +24287,7 @@ function toIsoString(value) {
24094
24287
  return Number.isNaN(date.getTime()) ? value : date.toISOString();
24095
24288
  }
24096
24289
  function sha256Hex2(value) {
24097
- return createHash10("sha256").update(value).digest("hex");
24290
+ return createHash12("sha256").update(value).digest("hex");
24098
24291
  }
24099
24292
  function normalizeHeaders(headers) {
24100
24293
  const result = {};
@@ -24146,7 +24339,7 @@ function toArrayBuffer(bytes) {
24146
24339
  return buffer;
24147
24340
  }
24148
24341
  // src/lib/station-snapshot.ts
24149
- import { createHash as createHash11 } from "crypto";
24342
+ import { createHash as createHash13 } from "crypto";
24150
24343
  import {
24151
24344
  copyFileSync as copyFileSync2,
24152
24345
  mkdirSync as mkdirSync15,
@@ -24154,12 +24347,12 @@ import {
24154
24347
  statSync as statSync13,
24155
24348
  writeFileSync as writeFileSync13
24156
24349
  } from "fs";
24157
- import { dirname as dirname14, isAbsolute as isAbsolute7, relative as relative8, resolve as resolve7, sep as sep7 } from "path";
24350
+ import { dirname as dirname16, isAbsolute as isAbsolute9, relative as relative8, resolve as resolve9, sep as sep7 } from "path";
24158
24351
 
24159
24352
  // src/lib/portable-snapshot-filter.ts
24160
24353
  import { readdirSync as readdirSync15, statSync as statSync12 } from "fs";
24161
24354
  import { homedir as homedir5 } from "os";
24162
- import { join as join27, sep as sep6 } from "path";
24355
+ import { join as join28, sep as sep6 } from "path";
24163
24356
  var SYNC_HOMES = [
24164
24357
  { name: "skills", subClass: "skills", agent: null },
24165
24358
  { name: "custom", subClass: "custom", agent: null },
@@ -24252,16 +24445,16 @@ function isPortableWithinSkill(relativeParts) {
24252
24445
  function homePathFor(definition, homesRoot) {
24253
24446
  const home = homesRoot ?? homedir5();
24254
24447
  if (definition.subClass === "skills" || definition.subClass === "custom") {
24255
- return join27(skillsDataRootForHome(home), definition.name);
24448
+ return join28(skillsDataRootForHome(home), definition.name);
24256
24449
  }
24257
24450
  if (definition.agent === "opencode") {
24258
- return join27(home, ".config", "opencode", "skills");
24451
+ return join28(home, ".config", "opencode", "skills");
24259
24452
  }
24260
- return join27(home, `.${definition.agent}`, "skills");
24453
+ return join28(home, `.${definition.agent}`, "skills");
24261
24454
  }
24262
24455
  function destinationFor(definition, stationId, relativePath) {
24263
- const category = definition.subClass === "agent-homes" ? join27("agent-homes", definition.agent ?? "") : definition.name;
24264
- return join27("resources", stationId, "skills", category, ...relativePath.split(sep6));
24456
+ const category = definition.subClass === "agent-homes" ? join28("agent-homes", definition.agent ?? "") : definition.name;
24457
+ return join28("resources", stationId, "skills", category, ...relativePath.split(sep6));
24265
24458
  }
24266
24459
  function walkEntries(absoluteRoot) {
24267
24460
  let entries;
@@ -24272,7 +24465,7 @@ function walkEntries(absoluteRoot) {
24272
24465
  }
24273
24466
  const output = [];
24274
24467
  for (const entry of entries) {
24275
- const childFull = join27(absoluteRoot, entry.name);
24468
+ const childFull = join28(absoluteRoot, entry.name);
24276
24469
  if (entry.isSymbolicLink()) {
24277
24470
  output.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
24278
24471
  continue;
@@ -24283,7 +24476,7 @@ function walkEntries(absoluteRoot) {
24283
24476
  }
24284
24477
  const nested = walkEntries(childFull);
24285
24478
  for (const item of nested) {
24286
- output.push({ ...item, relativePath: join27(entry.name, item.relativePath) });
24479
+ output.push({ ...item, relativePath: join28(entry.name, item.relativePath) });
24287
24480
  }
24288
24481
  continue;
24289
24482
  }
@@ -24321,7 +24514,7 @@ function validateStationId(stationId) {
24321
24514
  }
24322
24515
  }
24323
24516
  function sha256File(filePath) {
24324
- return createHash11("sha256").update(readFileSync22(filePath)).digest("hex");
24517
+ return createHash13("sha256").update(readFileSync22(filePath)).digest("hex");
24325
24518
  }
24326
24519
  function scanHome(definition, homesRoot) {
24327
24520
  const homePath = homePathFor(definition, homesRoot);
@@ -24392,7 +24585,7 @@ function humanHomes(scanned) {
24392
24585
  }));
24393
24586
  }
24394
24587
  function writeStationSnapshot(options) {
24395
- const repoRoot = resolve7(options.repoRoot ?? process.cwd());
24588
+ const repoRoot = resolve9(options.repoRoot ?? process.cwd());
24396
24589
  const { scanned, plans, totalBytes } = planStationSnapshot(options);
24397
24590
  const manifestFiles = plans.map((plan) => ({
24398
24591
  relativePath: plan.source.relativePath,
@@ -24418,9 +24611,9 @@ function writeStationSnapshot(options) {
24418
24611
  const conflicts = [];
24419
24612
  const untouched = [];
24420
24613
  for (const plan of plans) {
24421
- const destination = resolve7(repoRoot, plan.destination);
24614
+ const destination = resolve9(repoRoot, plan.destination);
24422
24615
  const destinationRelative = relative8(repoRoot, destination);
24423
- if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep7) || isAbsolute7(destinationRelative)) {
24616
+ if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep7) || isAbsolute9(destinationRelative)) {
24424
24617
  throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
24425
24618
  }
24426
24619
  let existingDigest = null;
@@ -24441,8 +24634,8 @@ function writeStationSnapshot(options) {
24441
24634
  }
24442
24635
  let written = 0;
24443
24636
  for (const plan of untouched) {
24444
- const destination = resolve7(repoRoot, plan.destination);
24445
- mkdirSync15(dirname14(destination), { recursive: true });
24637
+ const destination = resolve9(repoRoot, plan.destination);
24638
+ mkdirSync15(dirname16(destination), { recursive: true });
24446
24639
  copyFileSync2(plan.source.fullPath, destination);
24447
24640
  written += 1;
24448
24641
  }
@@ -24460,8 +24653,8 @@ function writeStationSnapshot(options) {
24460
24653
  },
24461
24654
  files: manifestFiles
24462
24655
  };
24463
- const manifestPath = resolve7(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
24464
- mkdirSync15(dirname14(manifestPath), { recursive: true });
24656
+ const manifestPath = resolve9(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
24657
+ mkdirSync15(dirname16(manifestPath), { recursive: true });
24465
24658
  writeFileSync13(manifestPath, `${JSON.stringify(manifest, null, 2)}
24466
24659
  `);
24467
24660
  return {
@@ -24472,7 +24665,7 @@ function writeStationSnapshot(options) {
24472
24665
  };
24473
24666
  }
24474
24667
  // src/lib/station-hydrate.ts
24475
- import { createHash as createHash12 } from "crypto";
24668
+ import { createHash as createHash14 } from "crypto";
24476
24669
  import {
24477
24670
  copyFileSync as copyFileSync3,
24478
24671
  mkdirSync as mkdirSync16,
@@ -24481,7 +24674,7 @@ import {
24481
24674
  statSync as statSync14,
24482
24675
  writeFileSync as writeFileSync14
24483
24676
  } from "fs";
24484
- import { dirname as dirname15, join as join28, resolve as resolve8, sep as sep8 } from "path";
24677
+ import { dirname as dirname17, join as join29, resolve as resolve10, sep as sep8 } from "path";
24485
24678
  var STATION_HYDRATION_MANIFEST_SCHEMA = "hasna.fleet-resources.skills-hydration-manifest/v1";
24486
24679
  var STATION_HYDRATION_PRODUCER = { name: "@hasna/skills", version: package_default.version };
24487
24680
  var MANIFEST_HASH_KEY_SEP = String.fromCharCode(0);
@@ -24489,11 +24682,11 @@ function fail(code, message, detail = []) {
24489
24682
  throw new StationSnapshotError(code, message, detail);
24490
24683
  }
24491
24684
  function snapshotRootFor(repoRoot, stationId) {
24492
- return join28(repoRoot, "resources", stationId, "skills");
24685
+ return join29(repoRoot, "resources", stationId, "skills");
24493
24686
  }
24494
24687
  function readSnapshotManifest(repoRoot, stationId) {
24495
24688
  const snapshotRoot = snapshotRootFor(repoRoot, stationId);
24496
- const manifestPath = join28(snapshotRoot, "sync-manifest.json");
24689
+ const manifestPath = join29(snapshotRoot, "sync-manifest.json");
24497
24690
  let manifest;
24498
24691
  try {
24499
24692
  manifest = JSON.parse(readFileSync23(manifestPath, "utf8"));
@@ -24520,7 +24713,7 @@ function planStationHydration(stationId, repoRoot) {
24520
24713
  const hashMismatches = [];
24521
24714
  const skippedByRule = [];
24522
24715
  for (const agent of SYNC_AGENTS) {
24523
- const agentRoot = join28(snapshotRoot, "agent-homes", agent);
24716
+ const agentRoot = join29(snapshotRoot, "agent-homes", agent);
24524
24717
  let identEntries;
24525
24718
  try {
24526
24719
  identEntries = readdirSync16(agentRoot, { withFileTypes: true });
@@ -24531,7 +24724,7 @@ function planStationHydration(stationId, repoRoot) {
24531
24724
  if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
24532
24725
  continue;
24533
24726
  }
24534
- const identRoot = join28(agentRoot, identEntry.name);
24727
+ const identRoot = join29(agentRoot, identEntry.name);
24535
24728
  const entries = walkEntries(identRoot);
24536
24729
  for (const entry of entries) {
24537
24730
  const relativeParts = [identEntry.name, ...entry.relativePath.split(sep8)];
@@ -24678,12 +24871,12 @@ function skillSha256(skill) {
24678
24871
  return sha256File(skill.files[0].winner.fullPath);
24679
24872
  }
24680
24873
  const joined = skill.files.map((file) => sha256File(file.winner.fullPath));
24681
- return createHash12("sha256").update(joined.sort().join(`
24874
+ return createHash14("sha256").update(joined.sort().join(`
24682
24875
  `)).digest("hex");
24683
24876
  }
24684
24877
  function writeStationHydration(options) {
24685
- const repoRoot = resolve8(options.repoRoot ?? process.cwd());
24686
- const cacheRoot = resolve8(options.cacheRoot ?? resolveCorpusRoot());
24878
+ const repoRoot = resolve10(options.repoRoot ?? process.cwd());
24879
+ const cacheRoot = resolve10(options.cacheRoot ?? resolveCorpusRoot());
24687
24880
  const plan = planStationHydration(options.stationId, repoRoot);
24688
24881
  const resultSkills = plan.winners.map((skill) => ({
24689
24882
  ident: skill.ident,
@@ -24716,7 +24909,7 @@ function writeStationHydration(options) {
24716
24909
  const toWrite = [];
24717
24910
  for (const skill of plan.winners) {
24718
24911
  for (const file of skill.files) {
24719
- const destination = join28(cacheRoot, skill.ident, file.withinIdent);
24912
+ const destination = join29(cacheRoot, skill.ident, file.withinIdent);
24720
24913
  const digest2 = sha256File(file.winner.fullPath);
24721
24914
  let existingDigest = null;
24722
24915
  try {
@@ -24737,7 +24930,7 @@ function writeStationHydration(options) {
24737
24930
  }
24738
24931
  let written = 0;
24739
24932
  for (const entry of toWrite) {
24740
- mkdirSync16(dirname15(entry.destination), { recursive: true });
24933
+ mkdirSync16(dirname17(entry.destination), { recursive: true });
24741
24934
  copyFileSync3(entry.fullPath, entry.destination);
24742
24935
  written += 1;
24743
24936
  }
@@ -24758,8 +24951,8 @@ function writeStationHydration(options) {
24758
24951
  },
24759
24952
  skills: resultSkills
24760
24953
  };
24761
- const hydrationManifestPath = join28(dirname15(cacheRoot), `hydration-${options.stationId}.json`);
24762
- mkdirSync16(dirname15(hydrationManifestPath), { recursive: true });
24954
+ const hydrationManifestPath = join29(dirname17(cacheRoot), `hydration-${options.stationId}.json`);
24955
+ mkdirSync16(dirname17(hydrationManifestPath), { recursive: true });
24763
24956
  writeFileSync14(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
24764
24957
  `);
24765
24958
  return {
@@ -24865,7 +25058,7 @@ async function requestInvitationEmail(origin, action, input) {
24865
25058
  }
24866
25059
 
24867
25060
  // src/lib/remote-private-publications.ts
24868
- import { createHash as createHash13 } from "crypto";
25061
+ import { createHash as createHash15 } from "crypto";
24869
25062
  var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
24870
25063
  var failures = {
24871
25064
  INVALID_REQUEST: [400, "The publication request is invalid."],
@@ -24912,7 +25105,7 @@ var hash2 = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
24912
25105
  var record6 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
24913
25106
  var exact = (v, keys) => record6(v) && Object.keys(v).sort().join(",") === keys.sort().join(",");
24914
25107
  var date = (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));
24915
- var publicationSha256 = (bytes) => createHash13("sha256").update(bytes).digest("hex");
25108
+ var publicationSha256 = (bytes) => createHash15("sha256").update(bytes).digest("hex");
24916
25109
  function checkedPublicationDeclaration(value) {
24917
25110
  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 || !hash2(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
24918
25111
  return bad();
@@ -25157,12 +25350,12 @@ class RemotePrivatePublicationsClient {
25157
25350
  previous = view;
25158
25351
  if (!["queued", "verifying"].includes(view.state) || Date.now() >= until)
25159
25352
  return view;
25160
- await new Promise((resolve9) => {
25353
+ await new Promise((resolve11) => {
25161
25354
  const timer = setTimeout(done, Math.min(1000, until - Date.now()));
25162
25355
  function done() {
25163
25356
  clearTimeout(timer);
25164
25357
  options.signal?.removeEventListener("abort", done);
25165
- resolve9();
25358
+ resolve11();
25166
25359
  }
25167
25360
  options.signal?.addEventListener("abort", done, { once: true });
25168
25361
  });
@@ -25394,23 +25587,23 @@ class RemoteSkillsAuthClient {
25394
25587
  }
25395
25588
  }
25396
25589
  // src/lib/private-publication-recovery.ts
25397
- import { constants as constants6, closeSync as closeSync7, fsyncSync as fsyncSync2, fstatSync as fstatSync7, lstatSync as lstatSync12, mkdirSync as mkdirSync17, openSync as openSync7, readSync as readSync5, realpathSync as realpathSync5, renameSync as renameSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync15 } from "fs";
25398
- import { dirname as dirname16, isAbsolute as isAbsolute8, join as join29, resolve as resolve9 } from "path";
25590
+ import { constants as constants7, closeSync as closeSync8, fsyncSync as fsyncSync2, fstatSync as fstatSync8, lstatSync as lstatSync14, mkdirSync as mkdirSync17, openSync as openSync8, readSync as readSync6, realpathSync as realpathSync5, renameSync as renameSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync15 } from "fs";
25591
+ import { dirname as dirname18, isAbsolute as isAbsolute10, join as join30, resolve as resolve11 } from "path";
25399
25592
  import { randomUUID as randomUUID3 } from "crypto";
25400
25593
  var fail2 = () => {
25401
25594
  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.");
25402
25595
  };
25403
25596
  function safeDirectory(directory) {
25404
- if (!isAbsolute8(directory) || directory !== resolve9(directory) || realpathSync5(directory) !== directory)
25597
+ if (!isAbsolute10(directory) || directory !== resolve11(directory) || realpathSync5(directory) !== directory)
25405
25598
  return fail2();
25406
- for (let path = directory;; path = dirname16(path)) {
25407
- const stat = lstatSync12(path);
25599
+ for (let path = directory;; path = dirname18(path)) {
25600
+ const stat = lstatSync14(path);
25408
25601
  if (!stat.isDirectory() || stat.isSymbolicLink())
25409
25602
  return fail2();
25410
- if (path === dirname16(path))
25603
+ if (path === dirname18(path))
25411
25604
  break;
25412
25605
  }
25413
- const own = lstatSync12(directory);
25606
+ const own = lstatSync14(directory);
25414
25607
  if ((own.mode & 63) !== 0 || process.getuid && own.uid !== process.getuid())
25415
25608
  return fail2();
25416
25609
  return { dev: own.dev, ino: own.ino };
@@ -25421,36 +25614,36 @@ function unchangedDirectory(directory, identity2) {
25421
25614
  return fail2();
25422
25615
  }
25423
25616
  function readOwned(directory, name, max) {
25424
- const identity2 = safeDirectory(directory), file = join29(directory, name);
25425
- const fd = openSync7(file, constants6.O_RDONLY | constants6.O_NOFOLLOW);
25617
+ const identity2 = safeDirectory(directory), file = join30(directory, name);
25618
+ const fd = openSync8(file, constants7.O_RDONLY | constants7.O_NOFOLLOW);
25426
25619
  try {
25427
- const stat = fstatSync7(fd);
25620
+ const stat = fstatSync8(fd);
25428
25621
  if (!stat.isFile() || stat.nlink !== 1 || stat.size > max || stat.size < 1 || (stat.mode & 63) !== 0 || process.getuid && stat.uid !== process.getuid())
25429
25622
  return fail2();
25430
25623
  const buffer = Buffer.alloc(Math.min(max, stat.size) + 1);
25431
25624
  let length = 0;
25432
25625
  while (length < buffer.length) {
25433
- const count = readSync5(fd, buffer, length, buffer.length - length, length);
25626
+ const count = readSync6(fd, buffer, length, buffer.length - length, length);
25434
25627
  if (count === 0)
25435
25628
  break;
25436
25629
  length += count;
25437
25630
  }
25438
- const bytes = buffer.subarray(0, length), after = fstatSync7(fd);
25631
+ const bytes = buffer.subarray(0, length), after = fstatSync8(fd);
25439
25632
  unchangedDirectory(directory, identity2);
25440
25633
  if (bytes.length !== stat.size || stat.size !== after.size || stat.mtimeMs !== after.mtimeMs || stat.ctimeMs !== after.ctimeMs)
25441
25634
  return fail2();
25442
25635
  return bytes;
25443
25636
  } finally {
25444
- closeSync7(fd);
25637
+ closeSync8(fd);
25445
25638
  }
25446
25639
  }
25447
25640
  function writeOwned(directory, name, bytes) {
25448
- const fd = openSync7(join29(directory, name), constants6.O_WRONLY | constants6.O_CREAT | constants6.O_EXCL | constants6.O_NOFOLLOW, 384);
25641
+ const fd = openSync8(join30(directory, name), constants7.O_WRONLY | constants7.O_CREAT | constants7.O_EXCL | constants7.O_NOFOLLOW, 384);
25449
25642
  try {
25450
25643
  writeFileSync15(fd, bytes);
25451
25644
  fsyncSync2(fd);
25452
25645
  } finally {
25453
- closeSync7(fd);
25646
+ closeSync8(fd);
25454
25647
  }
25455
25648
  }
25456
25649
  function save(directory, value) {
@@ -25458,12 +25651,12 @@ function save(directory, value) {
25458
25651
  writeOwned(directory, temporary, JSON.stringify(value) + `
25459
25652
  `);
25460
25653
  unchangedDirectory(directory, identity2);
25461
- renameSync6(join29(directory, temporary), join29(directory, "receipt.json"));
25462
- const fd = openSync7(directory, constants6.O_RDONLY | constants6.O_NOFOLLOW);
25654
+ renameSync6(join30(directory, temporary), join30(directory, "receipt.json"));
25655
+ const fd = openSync8(directory, constants7.O_RDONLY | constants7.O_NOFOLLOW);
25463
25656
  try {
25464
25657
  fsyncSync2(fd);
25465
25658
  } finally {
25466
- closeSync7(fd);
25659
+ closeSync8(fd);
25467
25660
  }
25468
25661
  }
25469
25662
  function bind(client, receipt) {
@@ -25471,23 +25664,23 @@ function bind(client, receipt) {
25471
25664
  throw new PrivatePublicationError("PUBLICATION_IDENTITY_CHANGED", "The fresh session does not match the recovery directory's server, account and membership.");
25472
25665
  }
25473
25666
  async function locked(directory, action) {
25474
- const identity2 = safeDirectory(directory), file = join29(directory, "operation.lock");
25667
+ const identity2 = safeDirectory(directory), file = join30(directory, "operation.lock");
25475
25668
  let fd;
25476
25669
  try {
25477
- fd = openSync7(file, constants6.O_WRONLY | constants6.O_CREAT | constants6.O_EXCL | constants6.O_NOFOLLOW, 384);
25670
+ fd = openSync8(file, constants7.O_WRONLY | constants7.O_CREAT | constants7.O_EXCL | constants7.O_NOFOLLOW, 384);
25478
25671
  } catch {
25479
25672
  throw new PrivatePublicationError("PUBLICATION_RECOVERY_BUSY", "Another operation holds this recovery directory. If it crashed, confirm that process has stopped before explicitly removing operation.lock and resuming.");
25480
25673
  }
25481
- const lock = fstatSync7(fd);
25674
+ const lock = fstatSync8(fd);
25482
25675
  try {
25483
25676
  writeFileSync15(fd, JSON.stringify({ pid: process.pid }) + `
25484
25677
  `);
25485
25678
  fsyncSync2(fd);
25486
25679
  return await action();
25487
25680
  } finally {
25488
- closeSync7(fd);
25681
+ closeSync8(fd);
25489
25682
  unchangedDirectory(directory, identity2);
25490
- const now = lstatSync12(file);
25683
+ const now = lstatSync14(file);
25491
25684
  if (now.dev !== lock.dev || now.ino !== lock.ino || !now.isFile() || now.isSymbolicLink())
25492
25685
  fail2();
25493
25686
  unlinkSync4(file);
@@ -25539,15 +25732,15 @@ async function preparePrivatePublication(client, sourceDirectory, recoveryDirect
25539
25732
  phase: "prepared",
25540
25733
  intent: null
25541
25734
  };
25542
- if (!isAbsolute8(recoveryDirectory) || resolve9(recoveryDirectory) !== recoveryDirectory || realpathSync5(dirname16(recoveryDirectory)) !== dirname16(recoveryDirectory))
25735
+ if (!isAbsolute10(recoveryDirectory) || resolve11(recoveryDirectory) !== recoveryDirectory || realpathSync5(dirname18(recoveryDirectory)) !== dirname18(recoveryDirectory))
25543
25736
  return fail2();
25544
25737
  mkdirSync17(recoveryDirectory, { mode: 448 });
25545
25738
  safeDirectory(recoveryDirectory);
25546
- const parent = openSync7(dirname16(recoveryDirectory), constants6.O_RDONLY | constants6.O_NOFOLLOW);
25739
+ const parent = openSync8(dirname18(recoveryDirectory), constants7.O_RDONLY | constants7.O_NOFOLLOW);
25547
25740
  try {
25548
25741
  fsyncSync2(parent);
25549
25742
  } finally {
25550
- closeSync7(parent);
25743
+ closeSync8(parent);
25551
25744
  }
25552
25745
  writeOwned(recoveryDirectory, "bundle.tgz", packed.bytes);
25553
25746
  save(recoveryDirectory, receipt);
@@ -25757,8 +25950,8 @@ async function createProfileClient() {
25757
25950
  return new HttpProfileClient(connection.apiKey, connection.apiOrigin);
25758
25951
  }
25759
25952
  // src/lib/skill-context.ts
25760
- import { createHash as createHash14 } from "crypto";
25761
- import { resolve as resolve10 } from "path";
25953
+ import { createHash as createHash16 } from "crypto";
25954
+ import { resolve as resolve12 } from "path";
25762
25955
 
25763
25956
  // src/lib/selected-document.ts
25764
25957
  var DOCUMENT_PATHS = ["SKILL.md", "README.md", "CLAUDE.md"];
@@ -25905,7 +26098,7 @@ async function buildSkillContext(input, options = {}) {
25905
26098
  }
25906
26099
  const promptWords = words(prompt);
25907
26100
  const loaded = new Set(resolved.session?.loaded ?? []);
25908
- const paths = [...input.paths ?? [], ...input.cwd ? [input.cwd, resolve10(input.cwd)] : []];
26101
+ const paths = [...input.paths ?? [], ...input.cwd ? [input.cwd, resolve12(input.cwd)] : []];
25909
26102
  const candidates = profile.selections.map((selection) => {
25910
26103
  const key = selectionKey(selection);
25911
26104
  if (input.restore && loaded.has(key))
@@ -25957,7 +26150,7 @@ ${content}`;
25957
26150
  `);
25958
26151
  const receipt = {
25959
26152
  schemaVersion: 1,
25960
- id: createHash14("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
26153
+ id: createHash16("sha256").update(JSON.stringify({ profile: profile.profileRevision, sessionId, selections, omitted })).digest("hex"),
25961
26154
  profileId: profile.profileId,
25962
26155
  profileRevision: profile.profileRevision,
25963
26156
  authority: profile.authority,
@@ -26158,6 +26351,8 @@ export {
26158
26351
  computeContentHash,
26159
26352
  completeSkillRun,
26160
26353
  clearRegistryCache,
26354
+ captureDiscoveryDirectories,
26355
+ captureDiscoveryByteSources,
26161
26356
  canonicalizeManifest,
26162
26357
  buildSkillsS3ObjectUrl,
26163
26358
  buildSkillsApiUrl,