@hasna/skills 0.10.70 → 0.10.72

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.
@@ -47,9 +47,9 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
47
47
  var __require = import.meta.require;
48
48
 
49
49
  // src/runtime-prerequisites.ts
50
- import { isAbsolute as isAbsolute5 } from "path";
50
+ import { isAbsolute as isAbsolute7 } from "path";
51
51
  // package.json
52
- var version = "0.10.70";
52
+ var version = "0.10.72";
53
53
  var package_default = {
54
54
  name: "@hasna/skills",
55
55
  version,
@@ -191,8 +191,8 @@ var package_default = {
191
191
  };
192
192
 
193
193
  // src/lib/agent-install-selection.ts
194
- import { lstatSync as lstatSync4 } from "fs";
195
- import { dirname as dirname5, join as join7, resolve as resolve6 } from "path";
194
+ import { lstatSync as lstatSync6 } from "fs";
195
+ import { dirname as dirname6, join as join7, resolve as resolve7 } from "path";
196
196
  import { homedir as homedir2 } from "os";
197
197
 
198
198
  // src/lib/agent-adapters.ts
@@ -284,11 +284,11 @@ var NATIVE_SKILL_ROOTS = [
284
284
  ];
285
285
 
286
286
  // src/lib/managed-policy.ts
287
- import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync3, openSync as openSync3, readSync as readSync3 } from "fs";
287
+ import { closeSync as closeSync5, constants as constants5, fstatSync as fstatSync5, lstatSync as lstatSync5, openSync as openSync5, readSync as readSync4 } from "fs";
288
288
  import { join as join6 } from "path";
289
289
 
290
290
  // src/lib/config.ts
291
- import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "fs";
291
+ import { closeSync, constants, existsSync as existsSync2, fstatSync, lstatSync, openSync, readFileSync, statSync, writeFileSync, mkdirSync } from "fs";
292
292
  import { join as join2, dirname } from "path";
293
293
 
294
294
  // src/lib/retired-settings.ts
@@ -424,7 +424,8 @@ function skillsDataRootForHome(home) {
424
424
  var ENUM_KEYS = {
425
425
  defaultAgent: ["claude", "codex", "gemini", "pi", "opencode", "all"],
426
426
  defaultScope: ["global", "project"],
427
- format: ["compact", "json", "csv"]
427
+ format: ["compact", "json", "csv"],
428
+ sessionPinMigration: ["auto", "off"]
428
429
  };
429
430
  var STRING_KEYS = ["extensionsDir"];
430
431
  function validKeys() {
@@ -495,6 +496,9 @@ function saveConfig(key, value, scope = "project") {
495
496
  if (!validKeys().includes(key)) {
496
497
  throw new Error(`Unknown config key: ${key}. Valid keys: ${validKeys().join(", ")}`);
497
498
  }
499
+ if (key === "sessionPinMigration" && scope !== "global") {
500
+ throw new Error(`${key} is read only from the global configuration. Use: skills config set ${key} <auto|off> --global`);
501
+ }
498
502
  const normalized = normalizeConfigValue(key, value);
499
503
  if (normalized === undefined) {
500
504
  const allowed = allowedValues(key);
@@ -549,14 +553,14 @@ function unsetConfig(key, scope = "project") {
549
553
  // src/lib/selection-cache.ts
550
554
  import { createHash as createHash2, randomUUID } from "crypto";
551
555
  import { spawnSync } from "child_process";
552
- import { closeSync, constants, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync3, readlinkSync, readSync, renameSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
556
+ import { closeSync as closeSync2, constants as constants2, fstatSync as fstatSync2, fsyncSync, linkSync, lstatSync as lstatSync2, mkdirSync as mkdirSync2, openSync as openSync2, readFileSync as readFileSync3, readlinkSync, readSync, renameSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
553
557
  import { hostname } from "os";
554
- import { dirname as dirname2, join as join4, parse, resolve as resolve2 } from "path";
558
+ import { dirname as dirname2, isAbsolute, join as join4, parse, resolve as resolve2 } from "path";
555
559
  import { isDeepStrictEqual } from "util";
556
560
 
557
561
  // src/lib/skill-bundle.ts
558
562
  import { createHash } from "crypto";
559
- import { readFileSync as readFileSync2, readdirSync, statSync } from "fs";
563
+ import { readFileSync as readFileSync2, readdirSync, statSync as statSync2 } from "fs";
560
564
  import { join as join3, relative } from "path";
561
565
  import { createGunzip } from "zlib";
562
566
 
@@ -774,7 +778,7 @@ function walk(root, current, out) {
774
778
  continue;
775
779
  if (isCredentialFile(entry.name))
776
780
  continue;
777
- const stats = statSync(absolute);
781
+ const stats = statSync2(absolute);
778
782
  out.push({
779
783
  path: rel,
780
784
  bytes: ownBytes(readFileSync2(absolute)),
@@ -1289,7 +1293,7 @@ function assertRegularPath(path, createParents = false) {
1289
1293
  for (let index = 0;index < segments.length; index++) {
1290
1294
  cursor = join4(cursor, segments[index]);
1291
1295
  try {
1292
- const stat = lstatSync(cursor);
1296
+ const stat = lstatSync2(cursor);
1293
1297
  if (stat.isSymbolicLink() || index < segments.length - 1 && !stat.isDirectory()) {
1294
1298
  throw new SkillSelectionError("UNSAFE_CACHE_PATH", "Skills cache paths must not traverse symlinks or non-directories.");
1295
1299
  }
@@ -1305,14 +1309,14 @@ function readRegularFile(path, limit) {
1305
1309
  assertRegularPath(path);
1306
1310
  let fd;
1307
1311
  try {
1308
- fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
1312
+ fd = openSync2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
1309
1313
  } catch (error) {
1310
1314
  if (error.code === "ENOENT")
1311
1315
  return null;
1312
1316
  throw error;
1313
1317
  }
1314
1318
  try {
1315
- const stat = fstatSync(fd);
1319
+ const stat = fstatSync2(fd);
1316
1320
  if (!stat.isFile() || stat.size > limit)
1317
1321
  throw new SkillSelectionError("INVALID_CACHE_FILE", "The Skills cache contains an invalid or oversized file.");
1318
1322
  const bytes = Buffer.alloc(stat.size + 1);
@@ -1327,7 +1331,7 @@ function readRegularFile(path, limit) {
1327
1331
  throw new SkillSelectionError("INVALID_CACHE_FILE", "The Skills cache file grew during its bounded read.");
1328
1332
  return bytes.subarray(0, size);
1329
1333
  } finally {
1330
- closeSync(fd);
1334
+ closeSync2(fd);
1331
1335
  }
1332
1336
  }
1333
1337
  function writeSelectionJson(path, value) {
@@ -1341,13 +1345,13 @@ function atomicWrite(path, bytes, mode, durable = false) {
1341
1345
  assertRegularPath(path, true);
1342
1346
  const temporary = join4(dirname2(path), `.selection-${randomUUID()}.tmp`);
1343
1347
  try {
1344
- const fd = openSync(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, mode);
1348
+ const fd = openSync2(temporary, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW, mode);
1345
1349
  try {
1346
1350
  writeFileSync2(fd, bytes);
1347
1351
  if (durable)
1348
1352
  fsyncSync(fd);
1349
1353
  } finally {
1350
- closeSync(fd);
1354
+ closeSync2(fd);
1351
1355
  }
1352
1356
  renameSync(temporary, path);
1353
1357
  if (durable)
@@ -1485,11 +1489,19 @@ function assertFreshCachedProfile(receipt, options = {}) {
1485
1489
  function projectSelectionLockPath(projectDir) {
1486
1490
  return join4(resolve2(projectDir), ".skills", SELECTION_LOCK_FILE);
1487
1491
  }
1488
- function readProjectSelection(projectDir) {
1489
- const receipt = readSelectionJson(projectSelectionLockPath(projectDir));
1490
- if (receipt)
1491
- validateSelectionReceipt(receipt);
1492
- return receipt;
1492
+ function readProjectSelectionSnapshot(projectDir) {
1493
+ const path = projectSelectionLockPath(projectDir);
1494
+ const bytes = readRegularFile(path, MAX_PROFILE_DOCUMENT_BYTES);
1495
+ if (!bytes)
1496
+ return null;
1497
+ let receipt;
1498
+ try {
1499
+ receipt = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
1500
+ } catch {
1501
+ throw new SkillSelectionError("INVALID_RECEIPT", "The Skills selection receipt is unreadable; sync the profile again.");
1502
+ }
1503
+ validateSelectionReceipt(receipt);
1504
+ return { receipt, lock: { path, sha256: sha256Hex(bytes) } };
1493
1505
  }
1494
1506
  function sessionReceiptPath(sessionId, options = {}) {
1495
1507
  if (!sessionId.trim() || sessionId.length > MAX_SKILL_SESSION_ID_CHARS)
@@ -1515,6 +1527,10 @@ function validateSkillSessionReceipt(receipt, sessionId) {
1515
1527
  if (receipt.parent.sessionId === sessionId)
1516
1528
  throw new SkillSelectionError("INVALID_RECEIPT", "A Skills child session cannot name itself as its parent.");
1517
1529
  }
1530
+ const lock = receipt.projectLock;
1531
+ if (lock !== undefined && (!lock || typeof lock !== "object" || Array.isArray(lock) || Object.keys(lock).sort().join(",") !== "path,sha256" || typeof lock.path !== "string" || !isAbsolute(lock.path) || lock.path.length > 4096 || !/^[a-f0-9]{64}$/.test(lock.sha256))) {
1532
+ throw new SkillSelectionError("INVALID_RECEIPT", "The Skills session receipt contains invalid project lock provenance.");
1533
+ }
1518
1534
  }
1519
1535
  function readSkillSessionSnapshotIfExists(sessionId, options = {}) {
1520
1536
  const path = sessionReceiptPath(sessionId, options), bytes = readRegularFile(path, MAX_PROFILE_DOCUMENT_BYTES);
@@ -1599,14 +1615,14 @@ function localLockIdentity() {
1599
1615
  }
1600
1616
  function readSessionLock(sessionId, options) {
1601
1617
  const path = `${sessionReceiptPath(sessionId, options)}.write-lock`;
1602
- const before = lstatSync(path, { throwIfNoEntry: false });
1618
+ const before = lstatSync2(path, { throwIfNoEntry: false });
1603
1619
  if (!before)
1604
1620
  return null;
1605
1621
  if (!before.isFile() || ![1, 2].includes(before.nlink) || (before.mode & 63) !== 0 || process.getuid && before.uid !== process.getuid()) {
1606
1622
  throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is not a private regular file; recovery requires review.");
1607
1623
  }
1608
1624
  const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
1609
- const after = lstatSync(path, { throwIfNoEntry: false });
1625
+ const after = lstatSync2(path, { throwIfNoEntry: false });
1610
1626
  if (!bytes || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs || before.mode !== after.mode || before.uid !== after.uid || before.gid !== after.gid || before.nlink !== after.nlink) {
1611
1627
  throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock changed during inspection; recovery requires review.");
1612
1628
  }
@@ -1623,7 +1639,7 @@ function readSessionLock(sessionId, options) {
1623
1639
  if (after.nlink === 2) {
1624
1640
  stagingPath = `${path}.${marker.operationId}.prepared`;
1625
1641
  assertRegularPath(stagingPath);
1626
- const staged = lstatSync(stagingPath, { throwIfNoEntry: false });
1642
+ const staged = lstatSync2(stagingPath, { throwIfNoEntry: false });
1627
1643
  if (marker.publication !== "hard-link-v1" || !staged?.isFile() || staged.dev !== after.dev || staged.ino !== after.ino || staged.nlink !== 2 || staged.mode !== after.mode || staged.uid !== after.uid || staged.gid !== after.gid) {
1628
1644
  throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock has an unbound publication link; recovery requires review.");
1629
1645
  }
@@ -1634,8 +1650,8 @@ function publishSessionLock(path, marker) {
1634
1650
  const stagingPath = `${path}.${marker.operationId}.prepared`;
1635
1651
  const bytes = Buffer.from(`${JSON.stringify({ ...marker, publication: "hard-link-v1" })}
1636
1652
  `);
1637
- const fd = openSync(stagingPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
1638
- const stat = fstatSync(fd);
1653
+ const fd = openSync2(stagingPath, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW, 384);
1654
+ const stat = fstatSync2(fd);
1639
1655
  let published = false, handedOff = false;
1640
1656
  try {
1641
1657
  writeFileSync2(fd, bytes);
@@ -1643,7 +1659,7 @@ function publishSessionLock(path, marker) {
1643
1659
  linkSync(stagingPath, path);
1644
1660
  published = true;
1645
1661
  syncDirectory(dirname2(path));
1646
- const staged = lstatSync(stagingPath);
1662
+ const staged = lstatSync2(stagingPath);
1647
1663
  if (!staged.isFile() || staged.dev !== stat.dev || staged.ino !== stat.ino) {
1648
1664
  throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "The initialized lock staging inode changed before publication completed.");
1649
1665
  }
@@ -1655,12 +1671,12 @@ function publishSessionLock(path, marker) {
1655
1671
  if (!handedOff) {
1656
1672
  try {
1657
1673
  for (const target of [...published ? [path] : [], stagingPath]) {
1658
- const current = lstatSync(target, { throwIfNoEntry: false });
1674
+ const current = lstatSync2(target, { throwIfNoEntry: false });
1659
1675
  if (current?.isFile() && current.dev === stat.dev && current.ino === stat.ino)
1660
1676
  unlinkSync(target);
1661
1677
  }
1662
1678
  } finally {
1663
- closeSync(fd);
1679
+ closeSync2(fd);
1664
1680
  }
1665
1681
  }
1666
1682
  }
@@ -1682,13 +1698,13 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
1682
1698
  const guardPath = `${lock.path}.recovery-guard`;
1683
1699
  let guardFd;
1684
1700
  try {
1685
- guardFd = openSync(guardPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
1701
+ guardFd = openSync2(guardPath, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW, 384);
1686
1702
  } catch (error) {
1687
1703
  if (error.code === "EEXIST")
1688
1704
  return null;
1689
1705
  throw error;
1690
1706
  }
1691
- const guardStat = fstatSync(guardFd);
1707
+ const guardStat = fstatSync2(guardFd);
1692
1708
  try {
1693
1709
  writeFileSync2(guardFd, `${JSON.stringify({ schemaVersion: 1, pid: process.pid, operationId: randomUUID() })}
1694
1710
  `);
@@ -1734,9 +1750,9 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
1734
1750
  syncDirectory(directory);
1735
1751
  syncDirectory(dirname2(lock.path));
1736
1752
  for (const path of archivedPaths) {
1737
- const before = lstatSync(path, { throwIfNoEntry: false });
1753
+ const before = lstatSync2(path, { throwIfNoEntry: false });
1738
1754
  const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
1739
- const after = lstatSync(path, { throwIfNoEntry: false });
1755
+ const after = lstatSync2(path, { throwIfNoEntry: false });
1740
1756
  if (!before?.isFile() || before.dev !== lock.dev || before.ino !== lock.ino || before.nlink !== archivedPaths.length || before.mtimeMs !== lock.mtimeMs || (before.mode & 63) !== 0 || process.getuid && before.uid !== process.getuid() || !bytes || sha256Hex(bytes) !== lock.sha256 || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs || before.mode !== after.mode || before.uid !== after.uid || before.gid !== after.gid || before.nlink !== after.nlink) {
1741
1757
  throw new Error("Archived lock ownership or bytes changed");
1742
1758
  }
@@ -1748,8 +1764,8 @@ function archiveSessionLock(lock, receipt, explicitLocalHost) {
1748
1764
  }
1749
1765
  return archivePath;
1750
1766
  } finally {
1751
- closeSync(guardFd);
1752
- const current = lstatSync(guardPath, { throwIfNoEntry: false });
1767
+ closeSync2(guardFd);
1768
+ const current = lstatSync2(guardPath, { throwIfNoEntry: false });
1753
1769
  if (current?.isFile() && current.dev === guardStat.dev && current.ino === guardStat.ino)
1754
1770
  unlinkSync(guardPath);
1755
1771
  }
@@ -1761,7 +1777,7 @@ function withSessionWriteLocks(sessionIds, options, action) {
1761
1777
  const owned = [];
1762
1778
  const assertOwned = () => {
1763
1779
  for (const lock of owned) {
1764
- const current = lstatSync(lock.path, { throwIfNoEntry: false });
1780
+ const current = lstatSync2(lock.path, { throwIfNoEntry: false });
1765
1781
  if (!current?.isFile() || current.dev !== lock.dev || current.ino !== lock.ino) {
1766
1782
  throw new SkillSelectionError("SESSION_WRITE_LOCK_CHANGED", "A session write lock changed during the operation; no replacement is permitted.");
1767
1783
  }
@@ -1805,12 +1821,12 @@ function withSessionWriteLocks(sessionIds, options, action) {
1805
1821
  for (const lock of owned.reverse()) {
1806
1822
  try {
1807
1823
  for (const path of [lock.path, lock.stagingPath]) {
1808
- const current = lstatSync(path, { throwIfNoEntry: false });
1824
+ const current = lstatSync2(path, { throwIfNoEntry: false });
1809
1825
  if (current?.isFile() && current.dev === lock.dev && current.ino === lock.ino)
1810
1826
  unlinkSync(path);
1811
1827
  }
1812
1828
  } finally {
1813
- closeSync(lock.fd);
1829
+ closeSync2(lock.fd);
1814
1830
  }
1815
1831
  }
1816
1832
  }
@@ -1861,21 +1877,21 @@ function writeSkillSession(receipt, expected, options = {}) {
1861
1877
  }
1862
1878
  function syncDirectory(path) {
1863
1879
  assertRegularPath(path);
1864
- const fd = openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
1880
+ const fd = openSync2(path, constants2.O_RDONLY | constants2.O_DIRECTORY | constants2.O_NOFOLLOW);
1865
1881
  try {
1866
1882
  fsyncSync(fd);
1867
1883
  } finally {
1868
- closeSync(fd);
1884
+ closeSync2(fd);
1869
1885
  }
1870
1886
  }
1871
1887
  function archiveBytes(path, bytes) {
1872
1888
  assertRegularPath(path, true);
1873
- const fd = openSync(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
1889
+ const fd = openSync2(path, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | constants2.O_NOFOLLOW, 384);
1874
1890
  try {
1875
1891
  writeFileSync2(fd, bytes);
1876
1892
  fsyncSync(fd);
1877
1893
  } finally {
1878
- closeSync(fd);
1894
+ closeSync2(fd);
1879
1895
  }
1880
1896
  }
1881
1897
  function replaceSkillSession(expected, replacement, plan, options = {}, beforeCommit = () => {}) {
@@ -1921,7 +1937,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
1921
1937
  }
1922
1938
 
1923
1939
  // src/lib/agent-policy-limits.ts
1924
- import { basename as basename3, isAbsolute as isAbsolute3, join as join5, resolve as resolve5, sep } from "path";
1940
+ import { basename as basename4, isAbsolute as isAbsolute5, join as join5, resolve as resolve6, sep as sep2 } from "path";
1925
1941
 
1926
1942
  // src/lib/codex-native-compatibility.ts
1927
1943
  var MEASURED_CODEX_CAPABILITIES = {
@@ -1948,25 +1964,25 @@ var SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAP
1948
1964
 
1949
1965
  // src/lib/claude-marketplace-entry-witness.ts
1950
1966
  import { createHash as createHash4 } from "crypto";
1951
- import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute2, resolve as resolve4 } from "path";
1967
+ import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute3, resolve as resolve4 } from "path";
1952
1968
 
1953
1969
  // src/lib/claude-settings-witness.ts
1954
1970
  import { createHash as createHash3 } from "crypto";
1955
- import { closeSync as closeSync2, constants as constants2, fstatSync as fstatSync2, lstatSync as lstatSync2, openSync as openSync2, readSync as readSync2 } from "fs";
1956
- import { basename, dirname as dirname3, isAbsolute, resolve as resolve3 } from "path";
1971
+ import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync3, openSync as openSync3, readSync as readSync2 } from "fs";
1972
+ import { basename, dirname as dirname3, isAbsolute as isAbsolute2, resolve as resolve3 } from "path";
1957
1973
  var CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
1958
1974
  function need(value, reason) {
1959
1975
  if (!value)
1960
1976
  throw new Error(`Claude settings witness ${reason}`);
1961
1977
  }
1962
1978
  function absolutePath(value) {
1963
- need(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute(value) && resolve3(value) === value, "requires a normalized absolute path");
1979
+ need(typeof value === "string" && value.length > 0 && value.length <= CLAUDE_SETTINGS_WITNESS_LIMITS.pathCharacters && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute2(value) && resolve3(value) === value, "requires a normalized absolute path");
1964
1980
  }
1965
1981
  var sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino && a.mode === b.mode && a.uid === b.uid && a.gid === b.gid && a.nlink === b.nlink && a.size === b.size && a.mtimeNs === b.mtimeNs && a.ctimeNs === b.ctimeNs;
1966
1982
  function parents(path) {
1967
1983
  const result = [];
1968
1984
  for (let at = dirname3(path);; at = dirname3(at)) {
1969
- const stat = lstatSync2(at, { bigint: true });
1985
+ const stat = lstatSync3(at, { bigint: true });
1970
1986
  need(stat.isDirectory() && !stat.isSymbolicLink(), "path traverses a symlink or non-directory");
1971
1987
  result.push([at, stat]);
1972
1988
  if (at === dirname3(at))
@@ -1977,12 +1993,12 @@ function readNativeSettingsWitnessFile(path, budget, filename = "settings.json")
1977
1993
  absolutePath(path);
1978
1994
  need(basename(path) === filename, `requires ${filename}`);
1979
1995
  need(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
1980
- const ancestors = parents(path), initial = lstatSync2(path, { bigint: true });
1996
+ const ancestors = parents(path), initial = lstatSync3(path, { bigint: true });
1981
1997
  need(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_SETTINGS_WITNESS_LIMITS.bytes), "is not a bounded regular file");
1982
1998
  need(initial.size <= BigInt(budget.remaining), "exceeds its aggregate byte limit");
1983
- const fd = openSync2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
1999
+ const fd = openSync3(path, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
1984
2000
  try {
1985
- const opened = fstatSync2(fd, { bigint: true });
2001
+ const opened = fstatSync3(fd, { bigint: true });
1986
2002
  need(opened.isFile() && sameFile(initial, opened), "changed during open");
1987
2003
  const bytes = Buffer.alloc(Number(opened.size) + 1);
1988
2004
  let length = 0;
@@ -1992,10 +2008,10 @@ function readNativeSettingsWitnessFile(path, budget, filename = "settings.json")
1992
2008
  break;
1993
2009
  length += count;
1994
2010
  }
1995
- const after = fstatSync2(fd, { bigint: true }), current = lstatSync2(path, { bigint: true });
2011
+ const after = fstatSync3(fd, { bigint: true }), current = lstatSync3(path, { bigint: true });
1996
2012
  need(current.isFile() && sameFile(opened, after) && sameFile(after, current) && BigInt(length) === opened.size, "changed during read");
1997
2013
  for (const [ancestor, before] of ancestors) {
1998
- const now = lstatSync2(ancestor, { bigint: true });
2014
+ const now = lstatSync3(ancestor, { bigint: true });
1999
2015
  need(now.isDirectory() && before.dev === now.dev && before.ino === now.ino && before.mode === now.mode, "parent changed during read");
2000
2016
  }
2001
2017
  budget.remaining -= length;
@@ -2005,7 +2021,7 @@ function readNativeSettingsWitnessFile(path, budget, filename = "settings.json")
2005
2021
  throw new Error("Claude settings witness is not strict UTF-8");
2006
2022
  }
2007
2023
  } finally {
2008
- closeSync2(fd);
2024
+ closeSync3(fd);
2009
2025
  }
2010
2026
  }
2011
2027
  function parse2(text) {
@@ -2375,7 +2391,7 @@ var stringArray = (value) => value?.kind === "array" && value.items.every(isStri
2375
2391
  var stringObject = (value) => value?.kind === "object" && value.entries.every(([, item]) => isString(item));
2376
2392
  function claudeMarketplaceEntrySourceValid(source) {
2377
2393
  const path = source?.path;
2378
- return typeof path === "string" && path.length > 0 && path.length <= PATH_CHARACTERS && !/[\x00-\x1f\x7f]/.test(path) && isAbsolute2(path) && resolve4(path) === path && basename2(path) === "marketplace.json" && basename2(dirname4(path)) === ".claude-plugin" && typeof source.marketplace === "string" && NAME.test(source.marketplace) && !source.marketplace.includes("..") && typeof source.plugin === "string" && NAME.test(source.plugin);
2394
+ return typeof path === "string" && path.length > 0 && path.length <= PATH_CHARACTERS && !/[\x00-\x1f\x7f]/.test(path) && isAbsolute3(path) && resolve4(path) === path && basename2(path) === "marketplace.json" && basename2(dirname4(path)) === ".claude-plugin" && typeof source.marketplace === "string" && NAME.test(source.marketplace) && !source.marketplace.includes("..") && typeof source.plugin === "string" && NAME.test(source.plugin);
2379
2395
  }
2380
2396
  function canonical2(value) {
2381
2397
  if (value.kind === "string")
@@ -2501,6 +2517,74 @@ function captureClaudeMarketplaceEntry(path, marketplace, plugin, budget = { rem
2501
2517
  return { path, hashMode: CLAUDE_MARKETPLACE_ENTRY_HASH_MODE, marketplace, plugin, sha256: hashClaudeMarketplaceEntry(text, { marketplace, plugin }) };
2502
2518
  }
2503
2519
 
2520
+ // src/lib/claude-plugin-cache-marker.ts
2521
+ import { createHash as createHash5 } from "crypto";
2522
+ import { closeSync as closeSync4, constants as constants4, fstatSync as fstatSync4, lstatSync as lstatSync4, openSync as openSync4, readSync as readSync3 } from "fs";
2523
+ import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute4, resolve as resolve5, sep } from "path";
2524
+ var CLAUDE_ORPHAN_MARKER_MODE = "claude-plugin-orphaned-at-v1";
2525
+ var CACHE = `${sep}.claude${sep}plugins${sep}cache${sep}`;
2526
+ function isClaudePluginCacheOrphanMarker(path) {
2527
+ if (typeof path !== "string" || basename3(path) !== ".orphaned_at" || !isAbsolute4(path) || resolve5(path) !== path || path.includes("\x00"))
2528
+ return false;
2529
+ const at = path.indexOf(CACHE);
2530
+ if (at < 0 || path.indexOf(CACHE, at + 1) >= 0)
2531
+ return false;
2532
+ const parts = path.slice(at + CACHE.length).split(sep);
2533
+ return parts.length === 4 && parts.every((part) => part && part !== "." && part !== "..");
2534
+ }
2535
+ function same(left, right) {
2536
+ return Boolean(right && right.isFile() && left.dev === right.dev && left.ino === right.ino && left.uid === right.uid && left.mode === right.mode && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs);
2537
+ }
2538
+ function safe(path) {
2539
+ for (let cursor = path;; cursor = dirname5(cursor)) {
2540
+ if (lstatSync4(cursor, { throwIfNoEntry: false })?.isSymbolicLink())
2541
+ throw new Error("Refusing symlink Claude orphan marker input");
2542
+ if (dirname5(cursor) === cursor)
2543
+ break;
2544
+ }
2545
+ }
2546
+ function hashClaudePluginCacheOrphanMarker(path, budget, changes) {
2547
+ if (!isClaudePluginCacheOrphanMarker(path))
2548
+ throw new Error("Expected a Claude plugin-cache version-root orphan marker");
2549
+ if (changes?.has(path))
2550
+ throw new Error("Claude orphan markers cannot intersect a planned write");
2551
+ safe(path);
2552
+ const before = lstatSync4(path, { throwIfNoEntry: false });
2553
+ if (!before)
2554
+ return null;
2555
+ const uid = process.getuid?.();
2556
+ if (!before.isFile() || uid === undefined || before.uid !== uid || before.size > 32)
2557
+ throw new Error("Invalid Claude orphan marker file or owner");
2558
+ const fd = openSync4(path, constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
2559
+ try {
2560
+ const opened = fstatSync4(fd);
2561
+ if (!same(before, opened))
2562
+ throw new Error("Claude orphan marker changed during open");
2563
+ const bytes = Buffer.alloc(33);
2564
+ let length = 0;
2565
+ while (length < bytes.length) {
2566
+ const count = readSync3(fd, bytes, length, bytes.length - length, null);
2567
+ if (!count)
2568
+ break;
2569
+ length += count;
2570
+ }
2571
+ if (!/^[1-9][0-9]{12}$/.test(bytes.subarray(0, length).toString("latin1")))
2572
+ throw new Error("Malformed Claude orphan marker timestamp");
2573
+ const after = fstatSync4(fd), current = lstatSync4(path, { throwIfNoEntry: false });
2574
+ safe(path);
2575
+ if (!same(opened, after) || !same(after, current) || length !== after.size)
2576
+ throw new Error("Claude orphan marker changed during read");
2577
+ if (budget) {
2578
+ if (length > budget.remaining)
2579
+ throw new Error("Raw discovery aggregate byte limit exceeded");
2580
+ budget.remaining -= length;
2581
+ }
2582
+ return createHash5("sha256").update(`${CLAUDE_ORPHAN_MARKER_MODE}\x00${path}\x00file`).digest("hex");
2583
+ } finally {
2584
+ closeSync4(fd);
2585
+ }
2586
+ }
2587
+
2504
2588
  // src/lib/agent-policy-limits.ts
2505
2589
  var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryPathLinks: 40, discoveryPathSteps: 256, discoveryPathSourceMetadataBytes: 64 * 1024, discoveryPathTotalMetadataBytes: 8 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
2506
2590
  function object(value) {
@@ -2555,7 +2639,7 @@ function assertAgentPolicyCollections(policy) {
2555
2639
  text(control.pluginId, 1024);
2556
2640
  text(control.namespace, 64);
2557
2641
  text(control.pluginParent);
2558
- requireBound(isAbsolute3(control.pluginParent) && resolve5(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
2642
+ requireBound(isAbsolute5(control.pluginParent) && resolve6(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
2559
2643
  if (control.remotePluginId !== undefined)
2560
2644
  requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
2561
2645
  if (control.skillsOnly !== undefined)
@@ -2573,7 +2657,7 @@ function assertAgentPolicyCollections(policy) {
2573
2657
  text(control.pluginId, 1024);
2574
2658
  text(control.namespace, 64);
2575
2659
  text(control.pluginParent);
2576
- requireBound(!ids.has(control.pluginId) && isAbsolute3(control.pluginParent) && resolve5(control.pluginParent) === control.pluginParent && ["local", "git", "npm"].includes(control.sourceType) && typeof control.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(control.sourceSha256));
2660
+ requireBound(!ids.has(control.pluginId) && isAbsolute5(control.pluginParent) && resolve6(control.pluginParent) === control.pluginParent && ["local", "git", "npm"].includes(control.sourceType) && typeof control.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(control.sourceSha256));
2577
2661
  ids.add(control.pluginId);
2578
2662
  }
2579
2663
  }
@@ -2582,7 +2666,7 @@ function assertAgentPolicyCollections(policy) {
2582
2666
  requireBound(agent === "gemini" && object(executable) && Object.keys(executable).length === 3 && executable.command === "gemini");
2583
2667
  for (const path of [executable.path, executable.target]) {
2584
2668
  text(path);
2585
- requireBound(!/[\x00-\x1f\x7f-\x9f\u200e\u200f\u202a-\u202e\u2066-\u2069]/.test(path) && isAbsolute3(path) && resolve5(path) === path);
2669
+ requireBound(!/[\x00-\x1f\x7f-\x9f\u200e\u200f\u202a-\u202e\u2066-\u2069]/.test(path) && isAbsolute5(path) && resolve6(path) === path);
2586
2670
  }
2587
2671
  }
2588
2672
  if (bridge.disabledBuiltins !== undefined)
@@ -2610,11 +2694,11 @@ function assertAgentPolicyCollections(policy) {
2610
2694
  requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).every((key) => ["roots", "parents", "directories"].includes(key)) && roots.length + parents2.length > 0 && new Set(roots).size === roots.length && new Set(parents2).size === parents2.length);
2611
2695
  for (const root of roots) {
2612
2696
  text(root);
2613
- requireBound(isAbsolute3(root) && resolve5(root) === root);
2697
+ requireBound(isAbsolute5(root) && resolve6(root) === root);
2614
2698
  }
2615
2699
  for (const parent of parents2) {
2616
2700
  text(parent);
2617
- requireBound(isAbsolute3(parent) && resolve5(parent) === parent);
2701
+ requireBound(isAbsolute5(parent) && resolve6(parent) === parent);
2618
2702
  }
2619
2703
  for (const directory of array(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
2620
2704
  requireBound(object(directory));
@@ -2634,7 +2718,7 @@ function assertAgentPolicyCollections(policy) {
2634
2718
  text(input.namespace, 64);
2635
2719
  text(input.pluginParent);
2636
2720
  text(input.sourceRoot);
2637
- requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) => isAbsolute3(path) && resolve5(path) === path) && typeof input.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(input.sourceSha256));
2721
+ requireBound(!ids.has(input.pluginId) && [input.pluginParent, input.sourceRoot].every((path) => isAbsolute5(path) && resolve6(path) === path) && typeof input.sourceSha256 === "string" && /^[a-f0-9]{64}$/.test(input.sourceSha256));
2638
2722
  ids.add(input.pluginId);
2639
2723
  }
2640
2724
  if (proof.directories !== undefined)
@@ -2648,14 +2732,14 @@ function assertAgentPolicyCollections(policy) {
2648
2732
  const proof = record(value.codexSemanticCache, 9), witness = record(proof.witness, 7);
2649
2733
  requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && [8, 9].includes(Object.keys(proof).length) && Object.keys(proof).every((key) => ["schema", "configPath", "hookRootAlias", "witness", "appOnlyParents", "previousPolicySha256", "supersededSources", "supersededDirectories", "reviewedHookParents"].includes(key)) && proof.schema === "hasna.codex-semantic-cache-discovery.v1" && typeof proof.previousPolicySha256 === "string" && /^[a-f0-9]{64}$/.test(proof.previousPolicySha256) && [6, 7].includes(Object.keys(witness).length) && Object.keys(witness).every((key) => ["schema", "cacheRoot", "controls", "appOnlyParents", "skills", "sha256", "hookParents"].includes(key)) && witness.schema === "hasna.codex-semantic-cache-witness.v1" && typeof witness.sha256 === "string" && /^[a-f0-9]{64}$/.test(witness.sha256));
2650
2734
  text(proof.configPath);
2651
- requireBound(isAbsolute3(proof.configPath) && resolve5(proof.configPath) === proof.configPath && basename3(proof.configPath) === "config.toml");
2735
+ requireBound(isAbsolute5(proof.configPath) && resolve6(proof.configPath) === proof.configPath && basename4(proof.configPath) === "config.toml");
2652
2736
  text(witness.cacheRoot);
2653
- requireBound(isAbsolute3(witness.cacheRoot) && resolve5(witness.cacheRoot) === witness.cacheRoot);
2737
+ requireBound(isAbsolute5(witness.cacheRoot) && resolve6(witness.cacheRoot) === witness.cacheRoot);
2654
2738
  if (proof.hookRootAlias !== null) {
2655
2739
  requireBound(object(proof.hookRootAlias) && Object.keys(proof.hookRootAlias).length === 2);
2656
2740
  for (const key of ["alias", "target"]) {
2657
2741
  text(proof.hookRootAlias[key]);
2658
- requireBound(isAbsolute3(proof.hookRootAlias[key]) && resolve5(proof.hookRootAlias[key]) === proof.hookRootAlias[key]);
2742
+ requireBound(isAbsolute5(proof.hookRootAlias[key]) && resolve6(proof.hookRootAlias[key]) === proof.hookRootAlias[key]);
2659
2743
  }
2660
2744
  }
2661
2745
  const parents2 = array(proof.appOnlyParents, 4096);
@@ -2663,7 +2747,7 @@ function assertAgentPolicyCollections(policy) {
2663
2747
  requireBound(object(parent) && Object.keys(parent).length === 5 && parent.role === "app-only");
2664
2748
  for (const key of ["pluginId", "namespace", "pluginParent", "remotePluginId"])
2665
2749
  text(parent[key]);
2666
- requireBound(isAbsolute3(parent.pluginParent) && resolve5(parent.pluginParent) === parent.pluginParent);
2750
+ requireBound(isAbsolute5(parent.pluginParent) && resolve6(parent.pluginParent) === parent.pluginParent);
2667
2751
  }
2668
2752
  if (proof.reviewedHookParents !== undefined)
2669
2753
  for (const parent of array(proof.reviewedHookParents, 4096)) {
@@ -2675,9 +2759,9 @@ function assertAgentPolicyCollections(policy) {
2675
2759
  text(parent.remotePluginId);
2676
2760
  else {
2677
2761
  text(parent.marketplaceSourcePath);
2678
- requireBound(typeof parent.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256) && isAbsolute3(parent.marketplaceSourcePath) && resolve5(parent.marketplaceSourcePath) === parent.marketplaceSourcePath && basename3(parent.marketplaceSourcePath) === "marketplace.json");
2762
+ requireBound(typeof parent.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(parent.marketplaceSha256) && isAbsolute5(parent.marketplaceSourcePath) && resolve6(parent.marketplaceSourcePath) === parent.marketplaceSourcePath && basename4(parent.marketplaceSourcePath) === "marketplace.json");
2679
2763
  }
2680
- requireBound(isAbsolute3(parent.pluginParent) && resolve5(parent.pluginParent) === parent.pluginParent);
2764
+ requireBound(isAbsolute5(parent.pluginParent) && resolve6(parent.pluginParent) === parent.pluginParent);
2681
2765
  }
2682
2766
  for (const row of [...array(witness.controls, 4096), ...array(witness.appOnlyParents, 4096)]) {
2683
2767
  requireBound(object(row));
@@ -2697,9 +2781,9 @@ function assertAgentPolicyCollections(policy) {
2697
2781
  text(row.remotePluginId);
2698
2782
  else {
2699
2783
  text(row.marketplaceSourcePath);
2700
- requireBound(typeof row.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(row.marketplaceSha256) && isAbsolute3(row.marketplaceSourcePath) && resolve5(row.marketplaceSourcePath) === row.marketplaceSourcePath && basename3(row.marketplaceSourcePath) === "marketplace.json");
2784
+ requireBound(typeof row.marketplaceSha256 === "string" && /^[a-f0-9]{64}$/.test(row.marketplaceSha256) && isAbsolute5(row.marketplaceSourcePath) && resolve6(row.marketplaceSourcePath) === row.marketplaceSourcePath && basename4(row.marketplaceSourcePath) === "marketplace.json");
2701
2785
  }
2702
- requireBound(isAbsolute3(row.pluginParent) && resolve5(row.pluginParent) === row.pluginParent);
2786
+ requireBound(isAbsolute5(row.pluginParent) && resolve6(row.pluginParent) === row.pluginParent);
2703
2787
  for (const key of ["manifestSha256", "hooksSha256"])
2704
2788
  requireBound(typeof row[key] === "string" && /^[a-f0-9]{64}$/.test(row[key]));
2705
2789
  if (row.installation === "remote-receipt")
@@ -2732,15 +2816,17 @@ function assertAgentPolicyCollections(policy) {
2732
2816
  text(source.path);
2733
2817
  requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
2734
2818
  if (source.hashMode !== undefined)
2735
- requireBound(["bytes", "path-bytes", "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "codex-settings-v5", "codex-settings-v6", "executable-v1", "sumi-settings-v1", "claude-marketplace-entry-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
2819
+ requireBound(["bytes", "path-bytes", CLAUDE_ORPHAN_MARKER_MODE, "claude-plugin-manifest-v1", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-settings-v4", "claude-marketplace-registry-v2", "codex-settings-v1", "codex-settings-v2", "codex-settings-v3", "codex-settings-v4", "codex-settings-v5", "codex-settings-v6", "executable-v1", "sumi-settings-v1", "claude-marketplace-entry-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (["bytes", CLAUDE_ORPHAN_MARKER_MODE].includes(source.hashMode) || source.sha256 !== null));
2820
+ if (source.hashMode === CLAUDE_ORPHAN_MARKER_MODE)
2821
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && isClaudePluginCacheOrphanMarker(source.path) && source.managedPlugins === undefined && source.reviewArtifact === undefined);
2736
2822
  if (source.hashMode === "executable-v1")
2737
- requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && value.sources.some((row) => row.hashMode === "codex-settings-v6") && isAbsolute3(source.path) && resolve5(source.path) === source.path);
2823
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && value.sources.some((row) => row.hashMode === "codex-settings-v6") && isAbsolute5(source.path) && resolve6(source.path) === source.path);
2738
2824
  if (source.hashMode === "codex-settings-v6") {
2739
2825
  const seen = new Set;
2740
2826
  for (const hook of array(source.hookSources, AGENT_POLICY_LIMITS.discoverySources)) {
2741
2827
  requireBound(object(hook) && Object.keys(hook).length === 2 && Object.keys(hook).every((key) => ["path", "sha256"].includes(key)));
2742
2828
  text(hook.path);
2743
- requireBound(isAbsolute3(hook.path) && resolve5(hook.path) === hook.path && !seen.has(hook.path));
2829
+ requireBound(isAbsolute5(hook.path) && resolve6(hook.path) === hook.path && !seen.has(hook.path));
2744
2830
  seen.add(hook.path);
2745
2831
  requireBound(hook.sha256 === null || typeof hook.sha256 === "string" && /^[a-f0-9]{64}$/.test(hook.sha256));
2746
2832
  requireBound(value.sources.some((row) => row.path === hook.path && row.sha256 === hook.sha256 && [undefined, "bytes"].includes(row.hashMode) && row.format === undefined && row.fields === undefined));
@@ -2748,15 +2834,15 @@ function assertAgentPolicyCollections(policy) {
2748
2834
  } else
2749
2835
  requireBound(source.hookSources === undefined);
2750
2836
  if (source.hashMode === "claude-plugin-manifest-v1")
2751
- requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve5(source.path) === source.path && source.path.endsWith(join5(sep, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
2837
+ requireBound(agent === "claude" && value.agent === "claude" && ["automatic", "reviewed"].includes(value.method) && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute5(source.path) && resolve6(source.path) === source.path && source.path.endsWith(join5(sep2, ".claude-plugin", "plugin.json")) && source.managedPlugins === undefined);
2752
2838
  if (source.hashMode === "sumi-settings-v1")
2753
- requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve5(source.path) === source.path && basename3(source.path) === "sumi.json");
2839
+ requireBound(agent === "sumi" && value.agent === "sumi" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute5(source.path) && resolve6(source.path) === source.path && basename4(source.path) === "sumi.json");
2754
2840
  if (source.hashMode === "codex-settings-v1" || source.hashMode === "codex-settings-v2" || (source.hashMode === "codex-settings-v3" || source.hashMode === "codex-settings-v4" || source.hashMode === "codex-settings-v5" || source.hashMode === "codex-settings-v6"))
2755
- requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve5(source.path) === source.path && basename3(source.path) === "config.toml");
2841
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute5(source.path) && resolve6(source.path) === source.path && basename4(source.path) === "config.toml");
2756
2842
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || (source.hashMode === "claude-settings-v3" || source.hashMode === "claude-settings-v4"))
2757
- requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve5(source.path) === source.path && basename3(source.path) === "settings.json");
2843
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute5(source.path) && resolve6(source.path) === source.path && basename4(source.path) === "settings.json");
2758
2844
  if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
2759
- requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute3(source.path) && resolve5(source.path) === source.path && basename3(source.path) === "known_marketplaces.json");
2845
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute5(source.path) && resolve6(source.path) === source.path && basename4(source.path) === "known_marketplaces.json");
2760
2846
  if (source.hashMode === "claude-marketplace-entry-v1")
2761
2847
  requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && claudeMarketplaceEntrySourceValid(source));
2762
2848
  if (source.hashMode === "claude-plugin-registry") {
@@ -2775,7 +2861,7 @@ function assertAgentPolicyCollections(policy) {
2775
2861
  requireBound(agent === "codex" && value.method === "reviewed" && source.hashMode === "bytes" && object(artifact) && Object.keys(artifact).length === 6 && Object.keys(artifact).every((key) => ["version", "kind", "storeRoot", "originalPath", "sha256", "catalogSha256"].includes(key)) && artifact.version === 1 && artifact.kind === "codex-native-catalog" && artifact.sha256 === source.sha256 && /^[a-f0-9]{64}$/.test(artifact.catalogSha256));
2776
2862
  for (const path of [artifact.storeRoot, artifact.originalPath]) {
2777
2863
  text(path);
2778
- requireBound(isAbsolute3(path) && resolve5(path) === path);
2864
+ requireBound(isAbsolute5(path) && resolve6(path) === path);
2779
2865
  }
2780
2866
  requireBound(source.path === join5(artifact.storeRoot, "agent-discovery", "artifacts", artifact.sha256, "catalog.json"));
2781
2867
  }
@@ -2831,14 +2917,14 @@ function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
2831
2917
  const path = join6(dataDir2, "agent-policy.json");
2832
2918
  let descriptor, observed = false;
2833
2919
  try {
2834
- const stat = lstatSync3(path);
2920
+ const stat = lstatSync5(path);
2835
2921
  observed = true;
2836
2922
  if (!stat.isFile() || stat.isSymbolicLink())
2837
2923
  throw refusal();
2838
2924
  if (stat.size > AGENT_POLICY_LIMITS.bytes)
2839
2925
  throw refusal("exceeds its size limit");
2840
- descriptor = openSync3(path, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0) | constants3.O_NONBLOCK);
2841
- const opened = fstatSync3(descriptor);
2926
+ descriptor = openSync5(path, constants5.O_RDONLY | (constants5.O_NOFOLLOW ?? 0) | constants5.O_NONBLOCK);
2927
+ const opened = fstatSync5(descriptor);
2842
2928
  if (!opened.isFile())
2843
2929
  throw refusal();
2844
2930
  if (opened.size > AGENT_POLICY_LIMITS.bytes)
@@ -2846,7 +2932,7 @@ function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
2846
2932
  const bytes = Buffer.allocUnsafe(AGENT_POLICY_LIMITS.bytes + 1);
2847
2933
  let length = 0;
2848
2934
  while (length < bytes.length) {
2849
- const count = readSync3(descriptor, bytes, length, bytes.length - length, null);
2935
+ const count = readSync4(descriptor, bytes, length, bytes.length - length, null);
2850
2936
  if (count === 0)
2851
2937
  break;
2852
2938
  length += count;
@@ -2857,7 +2943,7 @@ function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
2857
2943
  return { text: text2, value: parseManagedSkillPolicy(text2) };
2858
2944
  } catch (error) {
2859
2945
  if (error.code === "ENOENT" && !observed) {
2860
- if (lstatSync3(path, { throwIfNoEntry: false }) === undefined)
2946
+ if (lstatSync5(path, { throwIfNoEntry: false }) === undefined)
2861
2947
  return null;
2862
2948
  }
2863
2949
  if (error instanceof SkillSelectionError)
@@ -2865,7 +2951,7 @@ function readManagedSkillPolicySnapshot(dataDir2 = getDataDirReadOnly()) {
2865
2951
  throw refusal();
2866
2952
  } finally {
2867
2953
  if (descriptor !== undefined)
2868
- closeSync3(descriptor);
2954
+ closeSync5(descriptor);
2869
2955
  }
2870
2956
  }
2871
2957
  function readManagedSkillPolicy(dataDir2 = getDataDirReadOnly()) {
@@ -2887,7 +2973,7 @@ function detectedIntegrationAgents(options = {}) {
2887
2973
  throw new Error("Invalid existing bridge agent inventory");
2888
2974
  const present = (path) => {
2889
2975
  try {
2890
- return lstatSync4(path, { throwIfNoEntry: false }) !== undefined;
2976
+ return lstatSync6(path, { throwIfNoEntry: false }) !== undefined;
2891
2977
  } catch {
2892
2978
  throw new Error("Agent presence cannot be inspected; review native configuration before hook installation");
2893
2979
  }
@@ -2897,13 +2983,13 @@ function detectedIntegrationAgents(options = {}) {
2897
2983
  return true;
2898
2984
  if (agent === "sumi") {
2899
2985
  const xdg = env.XDG_CONFIG_HOME;
2900
- const xdgSumi = xdg?.trim() ? resolve6(options.cwd ?? process.cwd(), xdg === "~" ? home : xdg.startsWith("~/") || xdg.startsWith("~\\") ? resolve6(home, xdg.slice(2)) : xdg, "sumi") : null;
2901
- return ["SUMI_HOME", "SUMI_CONFIG_DIR", "SUMI_CONFIG", "SUMI_CONFIG_CONTENT"].some((key) => env[key] !== undefined) || Bun.which("sumi-paths", { PATH: env.PATH ?? "" }) !== null || NATIVE_SKILL_ROOTS.some(([provider, root]) => provider === "sumi" && present(join7(home, dirname5(root)))) || present(join7(home, ".config", "sumi")) || xdgSumi !== null && present(xdgSumi);
2986
+ const xdgSumi = xdg?.trim() ? resolve7(options.cwd ?? process.cwd(), xdg === "~" ? home : xdg.startsWith("~/") || xdg.startsWith("~\\") ? resolve7(home, xdg.slice(2)) : xdg, "sumi") : null;
2987
+ return ["SUMI_HOME", "SUMI_CONFIG_DIR", "SUMI_CONFIG", "SUMI_CONFIG_CONTENT"].some((key) => env[key] !== undefined) || Bun.which("sumi-paths", { PATH: env.PATH ?? "" }) !== null || NATIVE_SKILL_ROOTS.some(([provider, root]) => provider === "sumi" && present(join7(home, dirname6(root)))) || present(join7(home, ".config", "sumi")) || xdgSumi !== null && present(xdgSumi);
2902
2988
  }
2903
2989
  if (agent === "hermes" && env.HERMES_HOME !== undefined)
2904
2990
  return true;
2905
2991
  const adapter = AGENT_ADAPTERS[agent];
2906
- return present(join7(home, dirname5(adapter.config))) || NATIVE_SKILL_ROOTS.some(([provider, root]) => provider === agent && dirname5(root) === `.${agent}` && present(join7(home, dirname5(root))));
2992
+ return present(join7(home, dirname6(adapter.config))) || NATIVE_SKILL_ROOTS.some(([provider, root]) => provider === agent && dirname6(root) === `.${agent}` && present(join7(home, dirname6(root))));
2907
2993
  });
2908
2994
  if (!selected.length && !options.allowEmpty)
2909
2995
  throw new Error(`No configured or detected agents; select --agent <agent> explicitly (${INTEGRATION_AGENTS.join(", ")}).`);
@@ -2912,8 +2998,8 @@ function detectedIntegrationAgents(options = {}) {
2912
2998
 
2913
2999
  // src/lib/agent-sumi.ts
2914
3000
  import { spawnSync as spawnSync2 } from "child_process";
2915
- import { accessSync, constants as constants4, existsSync as existsSync4, lstatSync as lstatSync5, realpathSync, statSync as statSync2 } from "fs";
2916
- import { delimiter, isAbsolute as isAbsolute4, join as join8, resolve as resolve7 } from "path";
3001
+ import { accessSync, constants as constants6, existsSync as existsSync4, lstatSync as lstatSync7, realpathSync, statSync as statSync3 } from "fs";
3002
+ import { delimiter, isAbsolute as isAbsolute6, join as join8, resolve as resolve8 } from "path";
2917
3003
 
2918
3004
  // src/lib/hook-diagnostics.ts
2919
3005
  var CONTEXT_ERROR_CODES = new Set([
@@ -3002,10 +3088,10 @@ function recordWithKeys(value, keys) {
3002
3088
  return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
3003
3089
  }
3004
3090
  function absolutePath2(value) {
3005
- return typeof value === "string" && value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute4(value) && resolve7(value) === value;
3091
+ return typeof value === "string" && value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute6(value) && resolve8(value) === value;
3006
3092
  }
3007
3093
  function absoluteInput(value) {
3008
- return value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute4(value);
3094
+ return value.length > 0 && value.length <= 32768 && !/[\x00-\x1f\x7f]/.test(value) && isAbsolute6(value);
3009
3095
  }
3010
3096
  function resolveSumiPathsCommand(pathValue) {
3011
3097
  if (!pathValue)
@@ -3013,10 +3099,10 @@ function resolveSumiPathsCommand(pathValue) {
3013
3099
  for (const directory of pathValue.split(delimiter)) {
3014
3100
  if (!directory || /[\x00-\x1f\x7f]/.test(directory))
3015
3101
  continue;
3016
- const candidate = resolve7(directory, "sumi-paths");
3102
+ const candidate = resolve8(directory, "sumi-paths");
3017
3103
  try {
3018
- accessSync(candidate, constants4.X_OK);
3019
- if (statSync2(candidate).isFile())
3104
+ accessSync(candidate, constants6.X_OK);
3105
+ if (statSync3(candidate).isFile())
3020
3106
  return candidate;
3021
3107
  } catch {}
3022
3108
  }
@@ -3025,7 +3111,7 @@ function resolveSumiPathsCommand(pathValue) {
3025
3111
  function validPathPlan(value, home, cwd) {
3026
3112
  if (!recordWithKeys(value, ["schemaVersion", "kind", "home", "cwd", "roots", "legacyRoots", "configFiles", "skillRoots"]))
3027
3113
  return false;
3028
- if (value.schemaVersion !== 1 || value.kind !== "sumi-paths" || value.home !== resolve7(home) || value.cwd !== resolve7(cwd))
3114
+ if (value.schemaVersion !== 1 || value.kind !== "sumi-paths" || value.home !== resolve8(home) || value.cwd !== resolve8(cwd))
3029
3115
  return false;
3030
3116
  if (!recordWithKeys(value.roots, SUMI_PATH_KEYS) || !recordWithKeys(value.legacyRoots, SUMI_PATH_KEYS))
3031
3117
  return false;
@@ -3061,7 +3147,7 @@ function sumiPathPlan(home, cwd = process.cwd(), env = process.env) {
3061
3147
  throw new SumiPathResolverError("SUMI_PATH_INPUT_REFUSED");
3062
3148
  const runtimePath = process.env.PATH ?? "";
3063
3149
  const executable = resolveSumiPathsCommand(runtimePath);
3064
- if (!executable || !isAbsolute4(executable))
3150
+ if (!executable || !isAbsolute6(executable))
3065
3151
  throw new SumiPathResolverError("SUMI_PATH_RESOLVER_UNAVAILABLE");
3066
3152
  const childEnv = { PATH: runtimePath };
3067
3153
  for (const key of SUMI_PATH_SELECTORS)
@@ -3069,8 +3155,8 @@ function sumiPathPlan(home, cwd = process.cwd(), env = process.env) {
3069
3155
  childEnv[key] = env[key];
3070
3156
  let child2;
3071
3157
  try {
3072
- child2 = spawnSync2(executable, ["--json", "--home", resolve7(home), "--cwd", resolve7(cwd)], {
3073
- cwd: resolve7(cwd),
3158
+ child2 = spawnSync2(executable, ["--json", "--home", resolve8(home), "--cwd", resolve8(cwd)], {
3159
+ cwd: resolve8(cwd),
3074
3160
  env: childEnv,
3075
3161
  encoding: "buffer",
3076
3162
  timeout: 3000,
@@ -3102,7 +3188,7 @@ function selectedConfigDirectory(plan) {
3102
3188
  const canonical3 = plan.roots.config, legacy = plan.legacyRoots.config;
3103
3189
  if (legacy === null)
3104
3190
  return existsSync4(canonical3) ? realpathSync(canonical3) : canonical3;
3105
- const canonicalStat = lstatSync5(canonical3, { throwIfNoEntry: false }), legacyStat = lstatSync5(legacy, { throwIfNoEntry: false });
3191
+ const canonicalStat = lstatSync7(canonical3, { throwIfNoEntry: false }), legacyStat = lstatSync7(legacy, { throwIfNoEntry: false });
3106
3192
  if (canonicalStat && legacyStat && realpathSync(canonical3) !== realpathSync(legacy))
3107
3193
  throw new Error("Sumi config roots conflict; complete native home adoption before Skills integration");
3108
3194
  const selected = canonicalStat ? canonical3 : legacyStat ? legacy : canonical3;
@@ -3247,7 +3333,7 @@ function runtimePrerequisites(home, cwd) {
3247
3333
  checked: [],
3248
3334
  code: null
3249
3335
  };
3250
- if (!isAbsolute5(home) || !isAbsolute5(cwd))
3336
+ if (!isAbsolute7(home) || !isAbsolute7(cwd))
3251
3337
  return { ...result, code: "RUNTIME_PREREQUISITE_INPUT_REFUSED" };
3252
3338
  let agents;
3253
3339
  try {