@hasna/skills 0.10.8 → 0.10.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/index.js CHANGED
@@ -36868,7 +36868,7 @@ var package_default;
36868
36868
  var init_package = __esm(() => {
36869
36869
  package_default = {
36870
36870
  name: "@hasna/skills",
36871
- version: "0.10.8",
36871
+ version: "0.10.10",
36872
36872
  description: "Skills library for AI coding agents",
36873
36873
  type: "module",
36874
36874
  bin: {
@@ -36990,12 +36990,12 @@ var init_package = __esm(() => {
36990
36990
  },
36991
36991
  repository: {
36992
36992
  type: "git",
36993
- url: "https://github.com/hasna/apps.git",
36993
+ url: "https://github.com/hasna/skills.git",
36994
36994
  directory: "apps/skills"
36995
36995
  },
36996
- homepage: "https://github.com/hasna/apps/tree/main/apps/skills",
36996
+ homepage: "https://github.com/hasna/skills/tree/main/apps/skills",
36997
36997
  bugs: {
36998
- url: "https://github.com/hasna/apps/issues"
36998
+ url: "https://github.com/hasna/skills/issues"
36999
36999
  }
37000
37000
  };
37001
37001
  });
@@ -107308,14 +107308,864 @@ var init_remote_account2 = __esm(() => {
107308
107308
  init_remote_client();
107309
107309
  });
107310
107310
 
107311
+ // src/cli/commands/runtime-copyfile.ts
107312
+ import { createHash as createHash26, randomUUID as randomUUID9 } from "crypto";
107313
+ import { Readable } from "stream";
107314
+ import { createGunzip as createGunzip2 } from "zlib";
107315
+ import {
107316
+ chmodSync as chmodSync4,
107317
+ copyFileSync,
107318
+ existsSync as existsSync33,
107319
+ lstatSync as lstatSync27,
107320
+ mkdirSync as mkdirSync24,
107321
+ readFileSync as readFileSync37,
107322
+ readdirSync as readdirSync20,
107323
+ readlinkSync as readlinkSync4,
107324
+ realpathSync as realpathSync11,
107325
+ renameSync as renameSync13,
107326
+ symlinkSync,
107327
+ writeFileSync as writeFileSync23
107328
+ } from "fs";
107329
+ import { basename as basename13, delimiter, dirname as dirname29, isAbsolute as isAbsolute22, join as join54, relative as relative8, resolve as resolve22, sep as sep9 } from "path";
107330
+ function object8(value, code2) {
107331
+ if (!value || typeof value !== "object" || Array.isArray(value))
107332
+ throw new Error(code2);
107333
+ return value;
107334
+ }
107335
+ function hash5(bytes) {
107336
+ return createHash26("sha256").update(bytes).digest("hex");
107337
+ }
107338
+ function isWithin(parent, child) {
107339
+ const rel = relative8(parent, child);
107340
+ return rel === "" || !rel.startsWith(`..${sep9}`) && rel !== ".." && !isAbsolute22(rel);
107341
+ }
107342
+ function entryExists(path) {
107343
+ try {
107344
+ lstatSync27(path);
107345
+ return true;
107346
+ } catch {
107347
+ return false;
107348
+ }
107349
+ }
107350
+ function assertNoSymlinkAncestors(path) {
107351
+ const absolute = resolve22(path);
107352
+ const parts = absolute.split(sep9).filter(Boolean);
107353
+ let current = sep9;
107354
+ for (const part of parts) {
107355
+ current = join54(current, part);
107356
+ const stat3 = lstatSync27(current);
107357
+ if (stat3.isSymbolicLink())
107358
+ throw new Error("PATH_ANCESTOR_SYMLINK_UNSUPPORTED");
107359
+ if (!stat3.isDirectory())
107360
+ throw new Error("PATH_ANCESTOR_NOT_DIRECTORY");
107361
+ }
107362
+ }
107363
+ function acquireRuntimeLock(runtimeRoot) {
107364
+ const lockPath = join54(runtimeRoot, ".copyfile-update-lock");
107365
+ try {
107366
+ mkdirSync24(lockPath, { mode: 448 });
107367
+ } catch {
107368
+ throw new Error("RUNTIME_UPDATE_LOCKED");
107369
+ }
107370
+ try {
107371
+ writeJsonPrivate(join54(lockPath, "lock.json"), { operationId: randomUUID9(), pid: process.pid, startedAt: new Date().toISOString() });
107372
+ } catch {
107373
+ try {
107374
+ renameSync13(lockPath, join54(runtimeRoot, `.copyfile-update-lock-incomplete-${randomUUID9()}`));
107375
+ } catch {}
107376
+ throw new Error("RUNTIME_LOCK_INITIALIZATION_FAILED");
107377
+ }
107378
+ return () => {
107379
+ const released = join54(runtimeRoot, `.copyfile-update-lock-released-${randomUUID9()}`);
107380
+ writeJsonPrivate(join54(lockPath, "released.json"), { completedAt: new Date().toISOString() });
107381
+ renameSync13(lockPath, released);
107382
+ };
107383
+ }
107384
+ function packageBins(pkg) {
107385
+ const raw = pkg.bin;
107386
+ const entries = typeof raw === "string" ? [[PACKAGE_NAME.slice(PACKAGE_NAME.lastIndexOf("/") + 1), raw]] : raw && typeof raw === "object" && !Array.isArray(raw) ? Object.entries(raw) : [];
107387
+ const output2 = {};
107388
+ for (const [name, value] of entries) {
107389
+ if (!/^[a-zA-Z0-9._-]+$/.test(name) || typeof value !== "string" || !value || isAbsolute22(value)) {
107390
+ throw new Error("PACKAGE_BIN_INVALID");
107391
+ }
107392
+ const normalized = value.replaceAll("\\", "/");
107393
+ if (normalized.split("/").some((part) => part === ".." || part === "."))
107394
+ throw new Error("PACKAGE_BIN_INVALID");
107395
+ output2[name] = normalized;
107396
+ }
107397
+ if (!output2.skills)
107398
+ throw new Error("PACKAGE_BIN_INVALID");
107399
+ return output2;
107400
+ }
107401
+ function readPackage(path) {
107402
+ let data;
107403
+ try {
107404
+ data = object8(JSON.parse(readFileSync37(path, "utf8")), "PACKAGE_JSON_INVALID");
107405
+ } catch {
107406
+ throw new Error("PACKAGE_JSON_INVALID");
107407
+ }
107408
+ if (data.name !== PACKAGE_NAME || typeof data.version !== "string" || !new RegExp(SEMVER_PATTERN).test(data.version)) {
107409
+ throw new Error("PACKAGE_IDENTITY_INVALID");
107410
+ }
107411
+ return { data, version: data.version, bins: packageBins(data) };
107412
+ }
107413
+ function resolveRuntimeLayout(homeInput, pathInput) {
107414
+ const home = realpathSync11(homeInput);
107415
+ const runtimeRoot = join54(home, ".hasna", "skills", "runtime");
107416
+ assertNoSymlinkAncestors(runtimeRoot);
107417
+ const runtimeRootStat = lstatSync27(runtimeRoot);
107418
+ if (!runtimeRootStat.isDirectory() || runtimeRootStat.isSymbolicLink() || (runtimeRootStat.mode & 63) !== 0) {
107419
+ throw new Error("RUNTIME_ROOT_NOT_PRIVATE");
107420
+ }
107421
+ const selectedLauncher = Bun.which("skills", { PATH: pathInput });
107422
+ if (!selectedLauncher)
107423
+ throw new Error("ACTIVE_SKILLS_LAUNCHER_NOT_FOUND");
107424
+ const launcherPath = resolve22(selectedLauncher);
107425
+ assertNoSymlinkAncestors(dirname29(launcherPath));
107426
+ const launcherStat = lstatSync27(launcherPath);
107427
+ if (!launcherStat.isSymbolicLink())
107428
+ throw new Error("ACTIVE_SKILLS_LAUNCHER_NOT_MANAGED_LINK");
107429
+ const activeBinary = realpathSync11(launcherPath);
107430
+ const currentPackageRoot = dirname29(dirname29(activeBinary));
107431
+ assertNoSymlinkAncestors(currentPackageRoot);
107432
+ if (!isWithin(runtimeRoot, currentPackageRoot))
107433
+ throw new Error("ACTIVE_RUNTIME_OUTSIDE_COPYFILE_ROOT");
107434
+ const relativePackage = relative8(runtimeRoot, currentPackageRoot).split(sep9);
107435
+ if (relativePackage.length !== 4 || relativePackage[1] !== "node_modules" || relativePackage[2] !== "@hasna" || relativePackage[3] !== "skills") {
107436
+ throw new Error("ACTIVE_RUNTIME_LAYOUT_UNSUPPORTED");
107437
+ }
107438
+ const current = readPackage(join54(currentPackageRoot, "package.json"));
107439
+ if (!activeBinary.startsWith(`${currentPackageRoot}${sep9}`) || activeBinary !== join54(currentPackageRoot, current.bins.skills)) {
107440
+ throw new Error("ACTIVE_RUNTIME_ENTRYPOINT_MISMATCH");
107441
+ }
107442
+ const launchers = [];
107443
+ const seenDirectories = new Set;
107444
+ for (const rawDir of pathInput.split(delimiter).filter(Boolean)) {
107445
+ const dir = resolve22(rawDir);
107446
+ if (seenDirectories.has(dir))
107447
+ continue;
107448
+ seenDirectories.add(dir);
107449
+ for (const [name, target] of Object.entries(current.bins)) {
107450
+ const path = join54(dir, name);
107451
+ if (!existsSync33(path))
107452
+ continue;
107453
+ assertNoSymlinkAncestors(dir);
107454
+ let stat3;
107455
+ try {
107456
+ stat3 = lstatSync27(path);
107457
+ } catch {
107458
+ continue;
107459
+ }
107460
+ if (!stat3.isSymbolicLink())
107461
+ continue;
107462
+ let actual;
107463
+ try {
107464
+ actual = realpathSync11(path);
107465
+ } catch {
107466
+ throw new Error("LAUNCHER_TARGET_UNREADABLE");
107467
+ }
107468
+ if (actual !== join54(currentPackageRoot, target))
107469
+ continue;
107470
+ const oldLinkTarget = readlinkSync4(path);
107471
+ if (resolve22(dirname29(path), oldLinkTarget) !== actual)
107472
+ throw new Error("LAUNCHER_SYMLINK_CHAIN_UNSUPPORTED");
107473
+ const newTarget = join54(runtimeRoot, "__target__", "node_modules", "@hasna", "skills", target);
107474
+ launchers.push({ path, oldTarget: actual, oldLinkTarget, newTarget, backupPath: `${path}.skills-prev-${randomUUID9()}` });
107475
+ }
107476
+ }
107477
+ if (!launchers.some((item) => item.path === launcherPath))
107478
+ throw new Error("ACTIVE_LAUNCHER_NOT_IN_PATH_INVENTORY");
107479
+ if (new Set(launchers.map((item) => item.path)).size !== launchers.length)
107480
+ throw new Error("LAUNCHER_PATH_DUPLICATE");
107481
+ return { home, runtimeRoot, currentPackageRoot, currentVersion: current.version, bin: current.bins, launchers };
107482
+ }
107483
+ function verifyIntegrity(bytes, integrity) {
107484
+ const match = /^sha512-([A-Za-z0-9+/]+={0,2})$/.exec(integrity);
107485
+ if (!match)
107486
+ throw new Error("REGISTRY_INTEGRITY_UNSUPPORTED");
107487
+ const actual = createHash26("sha512").update(bytes).digest("base64");
107488
+ if (actual !== match[1])
107489
+ throw new Error("TARBALL_INTEGRITY_MISMATCH");
107490
+ }
107491
+ function expectedTarballUrl(input2, origin, version2) {
107492
+ if (typeof input2 !== "string")
107493
+ throw new Error("REGISTRY_TARBALL_URL_INVALID");
107494
+ let url2;
107495
+ try {
107496
+ url2 = new URL(input2);
107497
+ } catch {
107498
+ throw new Error("REGISTRY_TARBALL_URL_INVALID");
107499
+ }
107500
+ const base3 = new URL(origin);
107501
+ if (url2.protocol !== "https:" && base3.protocol === "https:")
107502
+ throw new Error("REGISTRY_TARBALL_URL_INVALID");
107503
+ if (url2.origin !== base3.origin || url2.username || url2.password || url2.search || url2.hash)
107504
+ throw new Error("REGISTRY_TARBALL_URL_INVALID");
107505
+ if (!url2.pathname.endsWith(`/skills-${version2}.tgz`))
107506
+ throw new Error("REGISTRY_TARBALL_URL_INVALID");
107507
+ return url2.toString();
107508
+ }
107509
+ function tarField(block, start, length) {
107510
+ const field2 = block.subarray(start, start + length);
107511
+ const end = field2.indexOf(0);
107512
+ return new TextDecoder("utf-8", { fatal: true }).decode(end < 0 ? field2 : field2.subarray(0, end));
107513
+ }
107514
+ function tarOctal2(block, start, length) {
107515
+ const raw = new TextDecoder().decode(block.subarray(start, start + length)).replace(/[\0 ]+$/g, "").trim();
107516
+ if (!raw)
107517
+ return 0;
107518
+ if (!/^[0-7]+$/.test(raw))
107519
+ throw new Error("TARBALL_TAR_NUMERIC_FIELD_INVALID");
107520
+ const value = Number.parseInt(raw, 8);
107521
+ if (!Number.isSafeInteger(value))
107522
+ throw new Error("TARBALL_TAR_NUMERIC_FIELD_INVALID");
107523
+ return value;
107524
+ }
107525
+ async function gunzipCapped(bytes, limit) {
107526
+ if (bytes[0] !== 31 || bytes[1] !== 139)
107527
+ throw new Error("TARBALL_GZIP_REQUIRED");
107528
+ const stream = Readable.from([Buffer.from(bytes)]).pipe(createGunzip2());
107529
+ const chunks = [];
107530
+ let size2 = 0;
107531
+ try {
107532
+ for await (const chunk2 of stream) {
107533
+ const part = Buffer.from(chunk2);
107534
+ size2 += part.byteLength;
107535
+ if (size2 > limit) {
107536
+ stream.destroy();
107537
+ throw new Error("TARBALL_EXPANDED_SIZE_INVALID");
107538
+ }
107539
+ chunks.push(part);
107540
+ }
107541
+ } finally {
107542
+ stream.destroy();
107543
+ }
107544
+ return Buffer.concat(chunks, size2);
107545
+ }
107546
+ async function preflightTarball(bytes, limits = {}) {
107547
+ const expanded = await gunzipCapped(bytes, limits.maxExpandedBytes ?? MAX_TARBALL_EXPANDED_BYTES);
107548
+ const entries = [];
107549
+ const paths = new Set;
107550
+ let offset = 0, ended = false, fileBytes = 0;
107551
+ while (offset + 512 <= expanded.byteLength) {
107552
+ const block = expanded.subarray(offset, offset + 512);
107553
+ if (block.every((byte) => byte === 0)) {
107554
+ ended = true;
107555
+ break;
107556
+ }
107557
+ if (entries.length >= (limits.maxEntries ?? MAX_TARBALL_ENTRIES))
107558
+ throw new Error("TARBALL_ENTRY_COUNT_INVALID");
107559
+ const expectedChecksum = tarOctal2(block, 148, 8);
107560
+ let checksum = 0;
107561
+ for (let i = 0;i < 512; i++)
107562
+ checksum += i >= 148 && i < 156 ? 32 : block[i];
107563
+ if (checksum !== expectedChecksum)
107564
+ throw new Error("TARBALL_TAR_CHECKSUM_INVALID");
107565
+ const name = tarField(block, 0, 100), prefix = tarField(block, 345, 155);
107566
+ const rawPath = `${prefix ? `${prefix}/` : ""}${name}`.replace(/\/$/, "");
107567
+ const pathParts2 = rawPath.split("/");
107568
+ if (rawPath !== "package" && !rawPath.startsWith("package/") || rawPath.startsWith("/") || rawPath.includes("\\") || pathParts2.some((part) => !part || part === "." || part === ".."))
107569
+ throw new Error("TARBALL_PATH_INVALID");
107570
+ if (paths.has(rawPath))
107571
+ throw new Error("TARBALL_DUPLICATE_PATH");
107572
+ paths.add(rawPath);
107573
+ const flag = block[156] === 0 ? "0" : String.fromCharCode(block[156]);
107574
+ const size2 = tarOctal2(block, 124, 12);
107575
+ const kind = flag === "0" ? "file" : flag === "5" ? "directory" : null;
107576
+ if (!kind || kind === "directory" && size2 !== 0 || rawPath === "package" && kind !== "directory")
107577
+ throw new Error("TARBALL_ENTRY_TYPE_UNSUPPORTED");
107578
+ fileBytes += size2;
107579
+ if (fileBytes > (limits.maxExpandedBytes ?? MAX_TARBALL_EXPANDED_BYTES))
107580
+ throw new Error("TARBALL_EXPANDED_SIZE_INVALID");
107581
+ entries.push({ path: rawPath, kind, size: size2 });
107582
+ const padded = Math.ceil(size2 / 512) * 512;
107583
+ offset += 512 + padded;
107584
+ if (offset > expanded.byteLength)
107585
+ throw new Error("TARBALL_TAR_TRUNCATED");
107586
+ }
107587
+ if (!ended || !expanded.subarray(offset).every((byte) => byte === 0))
107588
+ throw new Error("TARBALL_TAR_TERMINATOR_INVALID");
107589
+ return { entries, expandedBytes: expanded.byteLength };
107590
+ }
107591
+ async function downloadPackage(version2, stagingRoot, registryOrigin, fetcher) {
107592
+ const metadataUrl = `${registryOrigin}/@hasna%2fskills/${encodeURIComponent(version2)}`;
107593
+ const timeout = AbortSignal.timeout(30000);
107594
+ const metadataResponse = await fetcher(metadataUrl, { redirect: "error", signal: timeout });
107595
+ if (!metadataResponse.ok || new URL(metadataResponse.url || metadataUrl).origin !== new URL(registryOrigin).origin)
107596
+ throw new Error("REGISTRY_METADATA_FETCH_FAILED");
107597
+ const metadata = object8(JSON.parse(new TextDecoder().decode(await readBodyCapped(metadataResponse, 5 * 1024 * 1024, "REGISTRY_METADATA_SIZE_INVALID"))), "REGISTRY_METADATA_INVALID");
107598
+ if (metadata.name !== PACKAGE_NAME || metadata.version !== version2 || metadata._id !== `${PACKAGE_NAME}@${version2}`)
107599
+ throw new Error("REGISTRY_PACKAGE_IDENTITY_MISMATCH");
107600
+ const dist = object8(metadata.dist, "REGISTRY_DIST_INVALID");
107601
+ const integrity = typeof dist.integrity === "string" ? dist.integrity : "";
107602
+ const tarballUrl = expectedTarballUrl(dist.tarball, registryOrigin, version2);
107603
+ const response = await fetcher(tarballUrl, { redirect: "error", signal: AbortSignal.timeout(30000) });
107604
+ if (!response.ok || new URL(response.url || tarballUrl).origin !== new URL(registryOrigin).origin)
107605
+ throw new Error("REGISTRY_TARBALL_FETCH_FAILED");
107606
+ const bytes = await readBodyCapped(response, MAX_TARBALL_BYTES, "TARBALL_SIZE_INVALID");
107607
+ if (!bytes.byteLength)
107608
+ throw new Error("TARBALL_SIZE_INVALID");
107609
+ verifyIntegrity(bytes, integrity);
107610
+ const preflight = await preflightTarball(bytes);
107611
+ const archive = new Bun.Archive(bytes);
107612
+ const files = await archive.files();
107613
+ const regularFiles = preflight.entries.filter((entry) => entry.kind === "file");
107614
+ if (files.size !== regularFiles.length || regularFiles.some((entry) => files.get(entry.path)?.size !== entry.size))
107615
+ throw new Error("TARBALL_ARCHIVE_INTERPRETATION_MISMATCH");
107616
+ const packageJson = files.get("package/package.json");
107617
+ if (!packageJson)
107618
+ throw new Error("TARBALL_PACKAGE_JSON_MISSING");
107619
+ const unpacked = object8(JSON.parse(await packageJson.text()), "TARBALL_PACKAGE_JSON_INVALID");
107620
+ if (unpacked.name !== PACKAGE_NAME || unpacked.version !== version2)
107621
+ throw new Error("TARBALL_PACKAGE_IDENTITY_MISMATCH");
107622
+ const bins = packageBins(unpacked);
107623
+ const archiveRoot = join54(stagingRoot, "verified-archive");
107624
+ mkdirSync24(archiveRoot, { mode: 448 });
107625
+ await archive.extract(archiveRoot);
107626
+ const extractedPackage = join54(archiveRoot, "package");
107627
+ const extracted = readTree(extractedPackage, false);
107628
+ const tarballPath = join54(stagingRoot, "verified-package.tgz");
107629
+ writeFileSync23(tarballPath, bytes, { flag: "wx", mode: 384 });
107630
+ if (!readFileSync37(tarballPath).equals(Buffer.from(bytes)))
107631
+ throw new Error("TARBALL_READBACK_MISMATCH");
107632
+ return {
107633
+ tarballPath,
107634
+ tarballUrl,
107635
+ integrity,
107636
+ sha256: hash5(bytes),
107637
+ byteSize: bytes.byteLength,
107638
+ packageTreeSha256: extracted.digest,
107639
+ packageBins: bins
107640
+ };
107641
+ }
107642
+ async function readBodyCapped(response, limit, errorCode) {
107643
+ if (!response.body)
107644
+ throw new Error("HTTP_RESPONSE_BODY_MISSING");
107645
+ const reader = response.body.getReader();
107646
+ const chunks = [];
107647
+ let size2 = 0;
107648
+ try {
107649
+ for (;; ) {
107650
+ const { done, value } = await reader.read();
107651
+ if (done)
107652
+ break;
107653
+ size2 += value.byteLength;
107654
+ if (size2 > limit) {
107655
+ await reader.cancel();
107656
+ throw new Error(errorCode);
107657
+ }
107658
+ chunks.push(value);
107659
+ }
107660
+ } finally {
107661
+ reader.releaseLock();
107662
+ }
107663
+ const bytes = new Uint8Array(size2);
107664
+ let offset = 0;
107665
+ for (const chunk2 of chunks) {
107666
+ bytes.set(chunk2, offset);
107667
+ offset += chunk2.byteLength;
107668
+ }
107669
+ return bytes;
107670
+ }
107671
+ function readTree(root2, includeModes) {
107672
+ const base3 = realpathSync11(root2);
107673
+ const entries = [];
107674
+ const walk2 = (dir, prefix = "") => {
107675
+ for (const item of readdirSync20(dir).sort()) {
107676
+ const path = join54(dir, item);
107677
+ const rel = prefix ? `${prefix}/${item}` : item;
107678
+ const stat3 = lstatSync27(path);
107679
+ if (stat3.isSymbolicLink()) {
107680
+ const link2 = readlinkSync4(path);
107681
+ const resolved = resolve22(dirname29(path), link2);
107682
+ if (!isWithin(base3, resolved))
107683
+ throw new Error("TREE_SYMLINK_ESCAPES_ROOT");
107684
+ let physicalTarget;
107685
+ try {
107686
+ physicalTarget = realpathSync11(path);
107687
+ } catch {
107688
+ throw new Error("TREE_SYMLINK_TARGET_MISSING");
107689
+ }
107690
+ if (!isWithin(base3, physicalTarget))
107691
+ throw new Error("TREE_SYMLINK_ESCAPES_ROOT");
107692
+ entries.push({ path: rel, kind: "symlink", target: link2, ...includeModes ? { mode: stat3.mode & 511 } : {} });
107693
+ } else if (stat3.isDirectory()) {
107694
+ entries.push({ path: rel, kind: "directory", ...includeModes ? { mode: stat3.mode & 511 } : {} });
107695
+ walk2(path, rel);
107696
+ } else if (stat3.isFile()) {
107697
+ if (stat3.nlink !== 1)
107698
+ throw new Error("TREE_HARDLINK_UNSUPPORTED");
107699
+ const bytes = readFileSync37(path);
107700
+ entries.push({ path: rel, kind: "file", sha256: hash5(bytes), ...includeModes ? { mode: stat3.mode & 511 } : {} });
107701
+ } else
107702
+ throw new Error("TREE_ENTRY_TYPE_UNSUPPORTED");
107703
+ }
107704
+ };
107705
+ walk2(base3);
107706
+ return { entries, digest: hash5(new TextEncoder().encode(JSON.stringify(entries))) };
107707
+ }
107708
+ function normalizeModes(root2) {
107709
+ const walk2 = (dir) => {
107710
+ for (const item of readdirSync20(dir)) {
107711
+ const path = join54(dir, item);
107712
+ const stat3 = lstatSync27(path);
107713
+ if (stat3.isSymbolicLink())
107714
+ continue;
107715
+ if (stat3.isDirectory()) {
107716
+ chmodSync4(path, stat3.mode & 493);
107717
+ walk2(path);
107718
+ } else if (stat3.isFile()) {
107719
+ chmodSync4(path, stat3.mode & 493);
107720
+ } else
107721
+ throw new Error("RUNTIME_ENTRY_TYPE_UNSUPPORTED");
107722
+ }
107723
+ };
107724
+ walk2(root2);
107725
+ }
107726
+ function writeJsonPrivate(path, value) {
107727
+ const bytes = Buffer.from(`${JSON.stringify(value, null, 2)}
107728
+ `);
107729
+ writeFileSync23(path, bytes, { flag: "wx", mode: 384 });
107730
+ chmodSync4(path, 384);
107731
+ if (!readFileSync37(path).equals(bytes))
107732
+ throw new Error("PRIVATE_JSON_READBACK_MISMATCH");
107733
+ }
107734
+ function writePreimageManifest(stage, configs, launchers) {
107735
+ const path = join54(stage, "preimage", "manifest.json");
107736
+ const value = { schema: "skills.copyfile-preimage.v1", configs, launchers };
107737
+ const bytes = Buffer.from(`${JSON.stringify(value, null, 2)}
107738
+ `);
107739
+ writeFileSync23(path, bytes, { flag: "wx", mode: 256 });
107740
+ chmodSync4(path, 256);
107741
+ if (!readFileSync37(path).equals(bytes))
107742
+ throw new Error("PREIMAGE_MANIFEST_READBACK_MISMATCH");
107743
+ return hash5(bytes);
107744
+ }
107745
+ function verifyPreimageManifest(runtimePath, receipt, home) {
107746
+ const path = join54(runtimePath, "preimage", "manifest.json");
107747
+ const bytes = readFileSync37(path);
107748
+ if (hash5(bytes) !== receipt.preimageSha256)
107749
+ throw new Error("PREIMAGE_MANIFEST_DRIFT");
107750
+ const manifest = object8(JSON.parse(bytes.toString("utf8")), "PREIMAGE_MANIFEST_INVALID");
107751
+ const expected = { schema: "skills.copyfile-preimage.v1", configs: receipt.configs, launchers: receipt.launchers };
107752
+ if (JSON.stringify(manifest) !== JSON.stringify(expected))
107753
+ throw new Error("PREIMAGE_RECEIPT_MISMATCH");
107754
+ if (receipt.configs.length !== CONFIG_PREIMAGE_PATHS.length)
107755
+ throw new Error("PREIMAGE_CONFIG_SET_INVALID");
107756
+ for (const [index, relativePath] of CONFIG_PREIMAGE_PATHS.entries()) {
107757
+ const config2 = receipt.configs[index];
107758
+ if (config2.relativePath !== relativePath || config2.sourcePath !== join54(home, relativePath))
107759
+ throw new Error("PREIMAGE_CONFIG_BINDING_INVALID");
107760
+ if (config2.present) {
107761
+ const backup = join54(runtimePath, "preimage", "configs", relativePath);
107762
+ const stat3 = lstatSync27(backup);
107763
+ const saved = readFileSync37(backup);
107764
+ if (!stat3.isFile() || stat3.isSymbolicLink() || hash5(saved) !== config2.sha256 || saved.byteLength !== config2.byteSize)
107765
+ throw new Error("PREIMAGE_CONFIG_DRIFT");
107766
+ }
107767
+ }
107768
+ }
107769
+ function captureConfigPreimages(home, stage) {
107770
+ const root2 = join54(stage, "preimage", "configs");
107771
+ mkdirSync24(root2, { recursive: true, mode: 448 });
107772
+ return CONFIG_PREIMAGE_PATHS.map((relativePath) => {
107773
+ const sourcePath = join54(home, relativePath);
107774
+ if (!entryExists(sourcePath))
107775
+ return { sourcePath, relativePath, present: false };
107776
+ const stat3 = lstatSync27(sourcePath);
107777
+ if (!stat3.isFile() || stat3.isSymbolicLink())
107778
+ throw new Error("CONFIG_PREIMAGE_NOT_REGULAR_FILE");
107779
+ const bytes = readFileSync37(sourcePath);
107780
+ const backupPath = join54(root2, relativePath);
107781
+ mkdirSync24(dirname29(backupPath), { recursive: true, mode: 448 });
107782
+ writeFileSync23(backupPath, bytes, { flag: "wx", mode: 384 });
107783
+ chmodSync4(backupPath, 384);
107784
+ const readback = readFileSync37(backupPath);
107785
+ if (!readback.equals(bytes))
107786
+ throw new Error("CONFIG_PREIMAGE_READBACK_MISMATCH");
107787
+ return { sourcePath, relativePath, present: true, sha256: hash5(bytes), byteSize: bytes.byteLength };
107788
+ });
107789
+ }
107790
+ function assertConfigUnchanged(configs) {
107791
+ for (const config2 of configs) {
107792
+ const present = entryExists(config2.sourcePath);
107793
+ if (present !== config2.present)
107794
+ throw new Error("CONFIG_DRIFT_DURING_UPDATE");
107795
+ if (present) {
107796
+ const stat3 = lstatSync27(config2.sourcePath);
107797
+ if (!stat3.isFile() || stat3.isSymbolicLink() || hash5(readFileSync37(config2.sourcePath)) !== config2.sha256) {
107798
+ throw new Error("CONFIG_DRIFT_DURING_UPDATE");
107799
+ }
107800
+ }
107801
+ }
107802
+ }
107803
+ async function runNpm(args2, cwd2, home, staging) {
107804
+ const userNpmrc = join54(staging, "npm-user.npmrc"), globalNpmrc = join54(staging, "npm-global.npmrc");
107805
+ if (!existsSync33(userNpmrc))
107806
+ writeFileSync23(userNpmrc, `registry=${REGISTRY_ORIGIN}/
107807
+ ignore-scripts=true
107808
+ `, { mode: 384, flag: "wx" });
107809
+ if (!existsSync33(globalNpmrc))
107810
+ writeFileSync23(globalNpmrc, `registry=${REGISTRY_ORIGIN}/
107811
+ ignore-scripts=true
107812
+ `, { mode: 384, flag: "wx" });
107813
+ const env3 = {
107814
+ PATH: process.env.PATH ?? "",
107815
+ HOME: home,
107816
+ TMPDIR: staging,
107817
+ TMP: staging,
107818
+ TEMP: staging,
107819
+ CI: "1",
107820
+ NPM_CONFIG_USERCONFIG: userNpmrc,
107821
+ NPM_CONFIG_GLOBALCONFIG: globalNpmrc,
107822
+ NPM_CONFIG_CACHE: join54(staging, "npm-cache"),
107823
+ NPM_CONFIG_REGISTRY: `${REGISTRY_ORIGIN}/`,
107824
+ NPM_CONFIG_IGNORE_SCRIPTS: "true"
107825
+ };
107826
+ const proc = Bun.spawn(["npm", ...args2, "--registry", `${REGISTRY_ORIGIN}/`, "--ignore-scripts", "--no-audit", "--no-fund"], {
107827
+ cwd: cwd2,
107828
+ env: env3,
107829
+ stdin: "ignore",
107830
+ stdout: "ignore",
107831
+ stderr: "ignore"
107832
+ });
107833
+ const exitCode = await proc.exited;
107834
+ if (exitCode !== 0)
107835
+ throw new Error("DEPENDENCY_GRAPH_INSTALL_FAILED");
107836
+ }
107837
+ function comparePackageTrees(expectedRoot, installedRoot) {
107838
+ const expected = readTree(expectedRoot, false);
107839
+ const installed = readTree(installedRoot, false);
107840
+ if (JSON.stringify(expected.entries) !== JSON.stringify(installed.entries))
107841
+ throw new Error("INSTALLED_PACKAGE_BYTES_MISMATCH");
107842
+ return expected.digest;
107843
+ }
107844
+ function replaceLink(path, target, backupPath, oldLinkTarget) {
107845
+ const stat3 = lstatSync27(path);
107846
+ if (!stat3.isSymbolicLink() || entryExists(backupPath))
107847
+ throw new Error("LAUNCHER_PREIMAGE_DRIFT");
107848
+ const temp = `${path}.skills-next-${randomUUID9()}`;
107849
+ symlinkSync(oldLinkTarget, backupPath);
107850
+ if (readlinkSync4(backupPath) !== oldLinkTarget || realpathSync11(backupPath) !== resolve22(dirname29(path), oldLinkTarget))
107851
+ throw new Error("LAUNCHER_BACKUP_READBACK_MISMATCH");
107852
+ symlinkSync(target, temp);
107853
+ if (realpathSync11(temp) !== target)
107854
+ throw new Error("LAUNCHER_NEW_LINK_READBACK_MISMATCH");
107855
+ renameSync13(temp, path);
107856
+ }
107857
+ function atomicJson(path, value) {
107858
+ const temp = `${path}.next-${randomUUID9()}`;
107859
+ const bytes = Buffer.from(`${JSON.stringify(value, null, 2)}
107860
+ `);
107861
+ writeFileSync23(temp, bytes, { flag: "wx", mode: 384 });
107862
+ chmodSync4(temp, 384);
107863
+ renameSync13(temp, path);
107864
+ if (!readFileSync37(path).equals(bytes))
107865
+ throw new Error("RECEIPT_READBACK_MISMATCH");
107866
+ }
107867
+ function persistReceipt(path, value) {
107868
+ if (entryExists(path)) {
107869
+ const priorBytes = readFileSync37(path);
107870
+ const prior = object8(JSON.parse(priorBytes.toString("utf8")), "RECEIPT_INVALID");
107871
+ if (prior.id !== value.id || prior.schema !== "skills.copyfile-runtime-receipt.v1" || typeof prior.state !== "string" || !/^[a-z-]{1,32}$/.test(prior.state))
107872
+ throw new Error("RECEIPT_ID_DRIFT");
107873
+ const nextBytes = Buffer.from(`${JSON.stringify(value, null, 2)}
107874
+ `);
107875
+ if (priorBytes.equals(nextBytes))
107876
+ return;
107877
+ const historyRoot = join54(dirname29(path), "receipt-history");
107878
+ if (!entryExists(historyRoot))
107879
+ mkdirSync24(historyRoot, { mode: 448 });
107880
+ const historyStat = lstatSync27(historyRoot);
107881
+ if (!historyStat.isDirectory() || historyStat.isSymbolicLink() || (historyStat.mode & 63) !== 0)
107882
+ throw new Error("RECEIPT_HISTORY_NOT_PRIVATE");
107883
+ const historyPath = join54(historyRoot, `${value.id}-${String(prior.state)}-${randomUUID9()}.json`);
107884
+ writeFileSync23(historyPath, priorBytes, { flag: "wx", mode: 256 });
107885
+ chmodSync4(historyPath, 256);
107886
+ if (!readFileSync37(historyPath).equals(priorBytes))
107887
+ throw new Error("RECEIPT_HISTORY_READBACK_MISMATCH");
107888
+ }
107889
+ atomicJson(path, value);
107890
+ }
107891
+ async function updateCopyfileRuntime(version2, options = {}) {
107892
+ if (!new RegExp(SEMVER_PATTERN).test(version2))
107893
+ throw new Error("EXACT_VERSION_REQUIRED");
107894
+ if (!STABLE_SEMVER_PATTERN.test(version2))
107895
+ throw new Error("EXACT_STABLE_VERSION_REQUIRED");
107896
+ const home = resolve22(options.homeDir ?? process.env.HOME ?? "");
107897
+ if (!home || !existsSync33(home))
107898
+ throw new Error("HOME_NOT_FOUND");
107899
+ const layout = resolveRuntimeLayout(home, options.pathValue ?? process.env.PATH ?? "");
107900
+ if (process.platform === "win32")
107901
+ throw new Error("COPYFILE_RUNTIME_PLATFORM_UNSUPPORTED");
107902
+ const registryOrigin = options.registryOrigin ?? REGISTRY_ORIGIN;
107903
+ const origin = new URL(registryOrigin);
107904
+ if (origin.protocol !== "https:" && options.registryOrigin === undefined)
107905
+ throw new Error("REGISTRY_ORIGIN_INVALID");
107906
+ if (origin.username || origin.password || origin.search || origin.hash)
107907
+ throw new Error("REGISTRY_ORIGIN_INVALID");
107908
+ const runtimeRootStat = lstatSync27(layout.runtimeRoot);
107909
+ if ((runtimeRootStat.mode & 63) !== 0)
107910
+ throw new Error("RUNTIME_ROOT_NOT_PRIVATE");
107911
+ const id2 = randomUUID9();
107912
+ const releaseLock = acquireRuntimeLock(layout.runtimeRoot);
107913
+ const stagePath = join54(layout.runtimeRoot, `.stage-${version2}-${id2}`);
107914
+ const finalPath = join54(layout.runtimeRoot, `${version2}-copyfile`);
107915
+ const launchers = layout.launchers.map((item) => ({ ...item, newTarget: join54(finalPath, "node_modules", "@hasna", "skills", layout.bin[basename13(item.path)]) }));
107916
+ let switched = [];
107917
+ let moved = false;
107918
+ try {
107919
+ if (entryExists(finalPath))
107920
+ throw new Error("TARGET_RUNTIME_ALREADY_EXISTS");
107921
+ mkdirSync24(stagePath, { mode: 448 });
107922
+ chmodSync4(stagePath, 448);
107923
+ const configPreimages = captureConfigPreimages(home, stagePath);
107924
+ const fetcher = options.fetcher ?? fetch;
107925
+ const artifact = await downloadPackage(version2, stagePath, origin.origin, fetcher);
107926
+ if (artifact.packageBins && JSON.stringify(Object.entries(artifact.packageBins).sort()) !== JSON.stringify(Object.entries(layout.bin).sort())) {
107927
+ throw new Error("PACKAGE_BIN_SET_CHANGED");
107928
+ }
107929
+ const stagePackage = join54(stagePath, "install");
107930
+ mkdirSync24(stagePackage, { mode: 448 });
107931
+ copyFileSync(artifact.tarballPath, join54(stagePackage, "verified.tgz"));
107932
+ writeFileSync23(join54(stagePackage, "package.json"), JSON.stringify({ private: true, dependencies: { [PACKAGE_NAME]: "file:./verified.tgz" } }, null, 2), { mode: 384, flag: "wx" });
107933
+ await runNpm(["install", "--package-lock-only", "--prefix", stagePackage], stagePackage, home, stagePath);
107934
+ await runNpm(["ci", "--prefix", stagePackage], stagePackage, home, stagePath);
107935
+ const lockBytes = readFileSync37(join54(stagePackage, "package-lock.json"));
107936
+ const installedPackage = join54(stagePackage, "node_modules", "@hasna", "skills");
107937
+ const installed = readPackage(join54(installedPackage, "package.json"));
107938
+ if (installed.version !== version2 || JSON.stringify(Object.entries(installed.bins).sort()) !== JSON.stringify(Object.entries(layout.bin).sort())) {
107939
+ throw new Error("INSTALLED_PACKAGE_IDENTITY_MISMATCH");
107940
+ }
107941
+ const unpackedPackage = join54(stagePath, "verified-archive", "package");
107942
+ const packageTreeSha256 = comparePackageTrees(unpackedPackage, installedPackage);
107943
+ if (packageTreeSha256 !== artifact.packageTreeSha256)
107944
+ throw new Error("PACKAGE_TREE_HASH_MISMATCH");
107945
+ const nodeModules = join54(stagePackage, "node_modules");
107946
+ normalizeModes(nodeModules);
107947
+ chmodSync4(stagePath, 448);
107948
+ const runtimeTree = readTree(nodeModules, true);
107949
+ if (runtimeTree.entries.some((entry) => entry.kind === "file" && ((entry.mode ?? 0) & 18) !== 0)) {
107950
+ throw new Error("RUNTIME_FILE_MODE_UNSAFE");
107951
+ }
107952
+ assertConfigUnchanged(configPreimages);
107953
+ const targetPackageRoot = join54(finalPath, "node_modules", "@hasna", "skills");
107954
+ const preimageSha256 = writePreimageManifest(stagePath, configPreimages, launchers);
107955
+ const receipt = {
107956
+ schema: "skills.copyfile-runtime-receipt.v1",
107957
+ id: id2,
107958
+ state: "prepared",
107959
+ package: PACKAGE_NAME,
107960
+ currentVersion: layout.currentVersion,
107961
+ targetVersion: version2,
107962
+ registryOrigin: origin.origin,
107963
+ tarballUrl: artifact.tarballUrl,
107964
+ tarballIntegrity: artifact.integrity,
107965
+ tarballSha256: artifact.sha256,
107966
+ tarballBytes: artifact.byteSize,
107967
+ packageTreeSha256,
107968
+ installLockSha256: hash5(lockBytes),
107969
+ runtimeTreeSha256: runtimeTree.digest,
107970
+ runtimeRoot: layout.runtimeRoot,
107971
+ currentPackageRoot: layout.currentPackageRoot,
107972
+ targetPackageRoot,
107973
+ configs: configPreimages,
107974
+ preimageSha256,
107975
+ launchers,
107976
+ switchedLaunchers: [],
107977
+ rollbackCompletedLaunchers: []
107978
+ };
107979
+ writeJsonPrivate(join54(stagePath, "rollout-receipt.json"), receipt);
107980
+ const finalNodeModules = join54(stagePackage, "node_modules");
107981
+ const stagedNodeModules = join54(stagePath, "node_modules");
107982
+ renameSync13(finalNodeModules, stagedNodeModules);
107983
+ copyFileSync(join54(stagePackage, "package-lock.json"), join54(stagePath, "install-lock.json"));
107984
+ chmodSync4(join54(stagePath, "install-lock.json"), 384);
107985
+ const stagedTree = readTree(stagedNodeModules, true);
107986
+ if (stagedTree.digest !== runtimeTree.digest)
107987
+ throw new Error("STAGED_RUNTIME_READBACK_MISMATCH");
107988
+ receipt.targetPackageRoot = join54(finalPath, "node_modules", "@hasna", "skills");
107989
+ receipt.runtimeTreeSha256 = stagedTree.digest;
107990
+ persistReceipt(join54(stagePath, "rollout-receipt.json"), receipt);
107991
+ renameSync13(stagePath, finalPath);
107992
+ moved = true;
107993
+ const finalReceiptBytes = Buffer.from(`${JSON.stringify(receipt, null, 2)}
107994
+ `);
107995
+ if (!readFileSync37(join54(finalPath, "rollout-receipt.json")).equals(finalReceiptBytes))
107996
+ throw new Error("RECEIPT_MOVE_READBACK_MISMATCH");
107997
+ assertConfigUnchanged(configPreimages);
107998
+ for (const item of launchers) {
107999
+ if (realpathSync11(item.path) !== item.oldTarget)
108000
+ throw new Error("LAUNCHER_DRIFT_BEFORE_SWITCH");
108001
+ receipt.state = "switching";
108002
+ receipt.switchedLaunchers = [...receipt.switchedLaunchers, item.path];
108003
+ persistReceipt(join54(finalPath, "rollout-receipt.json"), receipt);
108004
+ replaceLink(item.path, item.newTarget, item.backupPath, item.oldLinkTarget);
108005
+ if (realpathSync11(item.path) !== item.newTarget || realpathSync11(item.backupPath) !== item.oldTarget) {
108006
+ throw new Error("LAUNCHER_SWITCH_READBACK_MISMATCH");
108007
+ }
108008
+ switched.push(item.path);
108009
+ options.onLauncherSwitched?.(item.path);
108010
+ }
108011
+ receipt.state = "switched";
108012
+ persistReceipt(join54(finalPath, "rollout-receipt.json"), receipt);
108013
+ return { updated: true, version: version2, currentVersion: layout.currentVersion, receiptId: id2, runtimeRoot: finalPath, tarballSha256: artifact.sha256, tarballIntegrity: artifact.integrity, runtimeTreeSha256: stagedTree.digest, launcherCount: launchers.length, configCount: configPreimages.length };
108014
+ } catch (error61) {
108015
+ if (switched.length > 0 || moved) {
108016
+ let rollbackFailed = false;
108017
+ for (const item of [...launchers].reverse()) {
108018
+ try {
108019
+ const currentTarget = realpathSync11(item.path);
108020
+ if (currentTarget === item.oldTarget && readlinkSync4(item.path) === item.oldLinkTarget)
108021
+ continue;
108022
+ if (currentTarget !== item.newTarget)
108023
+ throw new Error("LAUNCHER_CHANGED_DURING_ROLLBACK");
108024
+ const temp = `${item.path}.skills-rollback-${id2}`;
108025
+ const afterPath = `${item.path}.skills-after-${id2}`;
108026
+ if (entryExists(afterPath))
108027
+ throw new Error("ROLLBACK_PRESERVATION_PATH_EXISTS");
108028
+ symlinkSync(item.newTarget, afterPath);
108029
+ symlinkSync(item.oldLinkTarget, temp);
108030
+ renameSync13(temp, item.path);
108031
+ } catch {
108032
+ rollbackFailed = true;
108033
+ }
108034
+ }
108035
+ try {
108036
+ const receiptPath = join54(finalPath, "rollout-receipt.json");
108037
+ const receipt = JSON.parse(readFileSync37(receiptPath, "utf8"));
108038
+ receipt.state = rollbackFailed ? "rollback-required" : "rolled-back";
108039
+ persistReceipt(receiptPath, receipt);
108040
+ } catch {
108041
+ rollbackFailed = true;
108042
+ }
108043
+ throw new Error(rollbackFailed ? "UPDATE_FAILED_ROLLBACK_REQUIRED" : "UPDATE_FAILED_ROLLED_BACK");
108044
+ }
108045
+ throw error61 instanceof Error ? error61 : new Error("COPYFILE_UPDATE_FAILED");
108046
+ } finally {
108047
+ releaseLock();
108048
+ }
108049
+ }
108050
+ function rollbackCopyfileRuntime(receiptId, options = {}) {
108051
+ if (!/^[0-9a-f-]{36}$/.test(receiptId))
108052
+ throw new Error("RECEIPT_ID_INVALID");
108053
+ const home = resolve22(options.homeDir ?? process.env.HOME ?? "");
108054
+ if (!home || !existsSync33(home))
108055
+ throw new Error("HOME_NOT_FOUND");
108056
+ const runtimeRoot = join54(realpathSync11(home), ".hasna", "skills", "runtime");
108057
+ assertNoSymlinkAncestors(runtimeRoot);
108058
+ const rootStat = lstatSync27(runtimeRoot);
108059
+ if (!rootStat.isDirectory() || rootStat.isSymbolicLink() || (rootStat.mode & 63) !== 0)
108060
+ throw new Error("RUNTIME_ROOT_NOT_PRIVATE");
108061
+ const releaseLock = acquireRuntimeLock(runtimeRoot);
108062
+ try {
108063
+ const matches2 = readdirSync20(runtimeRoot).filter((name) => {
108064
+ if (!/^\d+\.\d+\.\d+-copyfile$/.test(name))
108065
+ return false;
108066
+ const receipt2 = join54(runtimeRoot, name, "rollout-receipt.json");
108067
+ if (!existsSync33(receipt2))
108068
+ return false;
108069
+ try {
108070
+ return JSON.parse(readFileSync37(receipt2, "utf8")).id === receiptId;
108071
+ } catch {
108072
+ return false;
108073
+ }
108074
+ });
108075
+ if (matches2.length !== 1)
108076
+ throw new Error("RECEIPT_NOT_FOUND_OR_AMBIGUOUS");
108077
+ const receiptPath = join54(runtimeRoot, matches2[0], "rollout-receipt.json");
108078
+ const receipt = object8(JSON.parse(readFileSync37(receiptPath, "utf8")), "RECEIPT_INVALID");
108079
+ if (receipt.schema !== "skills.copyfile-runtime-receipt.v1" || !["switched", "switching", "rollback-required"].includes(receipt.state) || receipt.runtimeRoot !== runtimeRoot) {
108080
+ throw new Error("RECEIPT_STATE_INVALID");
108081
+ }
108082
+ if (receipt.package !== PACKAGE_NAME || matches2[0] !== `${receipt.targetVersion}-copyfile` || !new RegExp(SEMVER_PATTERN).test(receipt.currentVersion) || !new RegExp(SEMVER_PATTERN).test(receipt.targetVersion))
108083
+ throw new Error("RECEIPT_BINDING_INVALID");
108084
+ const runtimePath = join54(runtimeRoot, matches2[0]);
108085
+ if (receipt.targetPackageRoot !== join54(runtimePath, "node_modules", "@hasna", "skills") || receipt.currentPackageRoot !== join54(runtimeRoot, `${receipt.currentVersion}-copyfile`, "node_modules", "@hasna", "skills"))
108086
+ throw new Error("RECEIPT_RUNTIME_BINDING_INVALID");
108087
+ if (readTree(join54(runtimePath, "node_modules"), true).digest !== receipt.runtimeTreeSha256) {
108088
+ throw new Error("ACTIVE_RUNTIME_TREE_DRIFT");
108089
+ }
108090
+ verifyPreimageManifest(runtimePath, receipt, home);
108091
+ const oldBins = readPackage(join54(receipt.currentPackageRoot, "package.json")).bins;
108092
+ const newPackage = readPackage(join54(receipt.targetPackageRoot, "package.json"));
108093
+ if (newPackage.version !== receipt.targetVersion || JSON.stringify(Object.entries(oldBins).sort()) !== JSON.stringify(Object.entries(newPackage.bins).sort()))
108094
+ throw new Error("RECEIPT_BIN_BINDING_INVALID");
108095
+ const switched = [];
108096
+ const seen = new Set;
108097
+ for (const item of receipt.launchers) {
108098
+ const bin = oldBins[basename13(item.path)];
108099
+ if (!bin || !isAbsolute22(item.path) || seen.has(item.path) || item.oldTarget !== join54(receipt.currentPackageRoot, bin) || item.newTarget !== join54(receipt.targetPackageRoot, bin) || !/^.+\.skills-prev-[0-9a-f-]{36}$/.test(item.backupPath) || !item.backupPath.startsWith(`${item.path}.skills-prev-`) || typeof item.oldLinkTarget !== "string" || resolve22(dirname29(item.path), item.oldLinkTarget) !== item.oldTarget)
108100
+ throw new Error("RECEIPT_LAUNCHER_BINDING_INVALID");
108101
+ assertNoSymlinkAncestors(dirname29(item.path));
108102
+ seen.add(item.path);
108103
+ const stat3 = lstatSync27(item.path);
108104
+ if (!stat3.isSymbolicLink())
108105
+ throw new Error("LAUNCHER_DRIFT_ROLLBACK_REFUSED");
108106
+ const activeTarget = realpathSync11(item.path);
108107
+ if (activeTarget !== item.newTarget && (activeTarget !== item.oldTarget || readlinkSync4(item.path) !== item.oldLinkTarget))
108108
+ throw new Error("LAUNCHER_DRIFT_ROLLBACK_REFUSED");
108109
+ const hasBackup = entryExists(item.backupPath);
108110
+ if (activeTarget === item.newTarget && !hasBackup)
108111
+ throw new Error("LAUNCHER_PREIMAGE_DRIFT_ROLLBACK_REFUSED");
108112
+ if (hasBackup) {
108113
+ const backup = lstatSync27(item.backupPath);
108114
+ if (!backup.isSymbolicLink() || readlinkSync4(item.backupPath) !== item.oldLinkTarget || realpathSync11(item.backupPath) !== item.oldTarget)
108115
+ throw new Error("LAUNCHER_PREIMAGE_DRIFT_ROLLBACK_REFUSED");
108116
+ }
108117
+ const afterPath = `${item.path}.skills-after-${receiptId}`;
108118
+ if (entryExists(afterPath)) {
108119
+ const after2 = lstatSync27(afterPath);
108120
+ if (!after2.isSymbolicLink() || realpathSync11(afterPath) !== item.newTarget)
108121
+ throw new Error("ROLLBACK_PRESERVATION_DRIFT");
108122
+ }
108123
+ switched.push(item);
108124
+ }
108125
+ for (const item of switched.reverse()) {
108126
+ const activeTarget = realpathSync11(item.path);
108127
+ if (activeTarget === item.oldTarget && readlinkSync4(item.path) === item.oldLinkTarget)
108128
+ continue;
108129
+ receipt.state = "rollback-required";
108130
+ persistReceipt(receiptPath, receipt);
108131
+ const afterPath = `${item.path}.skills-after-${receiptId}`;
108132
+ if (!entryExists(afterPath))
108133
+ symlinkSync(item.newTarget, afterPath);
108134
+ const temp = `${item.path}.skills-rollback-${receiptId}-${randomUUID9()}`;
108135
+ symlinkSync(item.oldLinkTarget, temp);
108136
+ renameSync13(temp, item.path);
108137
+ receipt.rollbackCompletedLaunchers = [...new Set([...receipt.rollbackCompletedLaunchers, item.path])];
108138
+ persistReceipt(receiptPath, receipt);
108139
+ }
108140
+ receipt.state = "rolled-back";
108141
+ persistReceipt(receiptPath, receipt);
108142
+ return { rolledBack: true, receiptId, restoredVersion: receipt.currentVersion, launcherCount: switched.length };
108143
+ } finally {
108144
+ releaseLock();
108145
+ }
108146
+ }
108147
+ var PACKAGE_NAME = "@hasna/skills", REGISTRY_ORIGIN = "https://registry.npmjs.org", STABLE_SEMVER_PATTERN, MAX_TARBALL_BYTES, MAX_TARBALL_EXPANDED_BYTES, MAX_TARBALL_ENTRIES = 20000, CONFIG_PREIMAGE_PATHS;
108148
+ var init_runtime_copyfile = __esm(() => {
108149
+ init_skill_contract();
108150
+ STABLE_SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
108151
+ MAX_TARBALL_BYTES = 100 * 1024 * 1024;
108152
+ MAX_TARBALL_EXPANDED_BYTES = 256 * 1024 * 1024;
108153
+ CONFIG_PREIMAGE_PATHS = [
108154
+ ".claude/settings.json",
108155
+ ".codex/config.toml",
108156
+ ".codex/hooks.json",
108157
+ ".hasna/skills/agent-policy.json"
108158
+ ];
108159
+ });
108160
+
107311
108161
  // src/cli/commands/runtime.ts
107312
108162
  var exports_runtime = {};
107313
108163
  __export(exports_runtime, {
107314
108164
  registerRuntime: () => registerRuntime
107315
108165
  });
107316
- import { createHash as createHash26, randomUUID as randomUUID9 } from "crypto";
107317
- import { lstatSync as lstatSync27, mkdirSync as mkdirSync24, readFileSync as readFileSync37, realpathSync as realpathSync11, writeFileSync as writeFileSync23 } from "fs";
107318
- import { basename as basename13, isAbsolute as isAbsolute22, join as join54 } from "path";
108166
+ import { createHash as createHash27, randomUUID as randomUUID10 } from "crypto";
108167
+ import { lstatSync as lstatSync28, mkdirSync as mkdirSync25, readFileSync as readFileSync38, realpathSync as realpathSync12, writeFileSync as writeFileSync24 } from "fs";
108168
+ import { basename as basename14, isAbsolute as isAbsolute23, join as join55 } from "path";
107319
108169
  import { createInterface } from "readline";
107320
108170
  function registerRuntime(parent) {
107321
108171
  parent.command("run").argument("<skill>", "Skill name").argument("[args...]", "Arguments to pass to the skill; use -- to end Skills options").allowUnknownOption(true).passThroughOptions(true).option("--json", "Output result as JSON", false).option("--remote", "Run on the configured server, using its catalog and quote", false).option("--target <target>", "Execution target: local or cloud").option("--secret-bindings <path>", "Reviewed, exact-selection local execution vault bindings (JSON file)").option("--secret-bindings-template", "Print a scoped bindings template without resolving secrets or running the skill", false).option("--input <json>", "Structured cloud execution input").option("--skill-version <version>", "Exact selected skill version").option("--selection-profile <id>", "Selected Skills profile").option("--session <id>", "Pinned Skills session").option("--cached", "Use the explicit verified selected cache", false).option("--yes", "Approve the server's quoted credit cost for this run", false).option("--idempotency-key <key>", "Reuse this key when retrying the same remote submission").option("--file <path>", "Attach a local input file to a remote run (repeatable)", (value, prior) => [...prior, value], []).option("--wait", "Poll remote runs until a terminal status", false).option("--poll-interval-ms <ms>", "Remote polling interval in milliseconds", "1000").option("--poll-timeout-ms <ms>", "Maximum time to wait for a remote run", "300000").description("Run a skill directly").action(async (name, args2, options, command) => {
@@ -107354,9 +108204,9 @@ function registerRuntime(parent) {
107354
108204
  if (!expected)
107355
108205
  throw new Error("Cloud artifact not found");
107356
108206
  const bytes = await client2.download(id2, name);
107357
- if (bytes.length !== expected.byteSize || createHash26("sha256").update(bytes).digest("hex") !== expected.sha256)
108207
+ if (bytes.length !== expected.byteSize || createHash27("sha256").update(bytes).digest("hex") !== expected.sha256)
107358
108208
  throw new Error("Cloud artifact integrity verification failed");
107359
- writeFileSync23(options.output, bytes, { flag: "wx", mode: 384 });
108209
+ writeFileSync24(options.output, bytes, { flag: "wx", mode: 384 });
107360
108210
  console.log(JSON.stringify({ path: options.output, sha256: expected.sha256, byteSize: bytes.length }));
107361
108211
  } catch (error61) {
107362
108212
  console.error(JSON.stringify({ error: error61.message }));
@@ -107384,7 +108234,40 @@ function registerRuntime(parent) {
107384
108234
  });
107385
108235
  const setup = parent.command("setup").description("Point this CLI at a Skills API server, or register agent integrations").option("--api-url <url>", "Skills API origin to send remote work to").option("--global", "Accepted for compatibility; the API origin is always per-user", false).option("--json", "Output setup result as JSON", false).action(async (options) => handleSetup(options));
107386
108236
  setup.command("agents").option("--json", "Output registration result as JSON", false).description("Register the Skills MCP server with all supported agents").action(async (options) => handleMcp({ register: "all", json: options.json }));
107387
- parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).action(async (options) => {
108237
+ parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).option("--version <version>", "Install this exact @hasna/skills version as a staged copyfile runtime").option("--rollback <receipt-id>", "Restore launcher links from an exact copyfile rollout receipt").action(async (options) => {
108238
+ if (options.version && options.rollback) {
108239
+ console.error(JSON.stringify({ error: "UPDATE_AND_ROLLBACK_ARE_MUTUALLY_EXCLUSIVE" }));
108240
+ process.exitCode = 1;
108241
+ return;
108242
+ }
108243
+ if (options.version) {
108244
+ try {
108245
+ const result2 = await updateCopyfileRuntime(options.version);
108246
+ if (options.json)
108247
+ console.log(JSON.stringify(result2));
108248
+ else
108249
+ console.log(source_default.green(`Updated @hasna/skills to exact version ${options.version} via copyfile runtime.`));
108250
+ } catch (error61) {
108251
+ const message = error61 instanceof Error && /^[A-Z0-9_]+$/.test(error61.message) ? error61.message : "COPYFILE_UPDATE_FAILED";
108252
+ console.error(JSON.stringify({ updated: false, error: message }));
108253
+ process.exitCode = 1;
108254
+ }
108255
+ return;
108256
+ }
108257
+ if (options.rollback) {
108258
+ try {
108259
+ const result2 = rollbackCopyfileRuntime(options.rollback);
108260
+ if (options.json)
108261
+ console.log(JSON.stringify(result2));
108262
+ else
108263
+ console.log(source_default.green(`Restored Skills runtime launchers to ${String(result2.restoredVersion)}.`));
108264
+ } catch (error61) {
108265
+ const message = error61 instanceof Error && /^[A-Z0-9_]+$/.test(error61.message) ? error61.message : "COPYFILE_ROLLBACK_FAILED";
108266
+ console.error(JSON.stringify({ rolledBack: false, error: message }));
108267
+ process.exitCode = 1;
108268
+ }
108269
+ return;
108270
+ }
107388
108271
  if (process.env.SKILLS_TEST_MODE === "1") {
107389
108272
  if (options.json)
107390
108273
  console.log(JSON.stringify({ updated: false, error: "Self-update disabled in test mode" }));
@@ -107442,14 +108325,14 @@ Updating ${name}...
107442
108325
  async function readUpdatedVersion(bunExecutable) {
107443
108326
  const output2 = await readUpdateCommand([bunExecutable, "pm", "bin", "-g"]);
107444
108327
  const globalBin = output2.replace(/\r?\n$/, "");
107445
- if (!globalBin || !isAbsolute22(globalBin) || /[\r\n\0]/.test(globalBin))
108328
+ if (!globalBin || !isAbsolute23(globalBin) || /[\r\n\0]/.test(globalBin))
107446
108329
  throw new Error("Update bin discovery failed");
107447
108330
  const installed = Bun.which("skills", { PATH: globalBin, cwd: globalBin });
107448
108331
  const selected = Bun.which("skills");
107449
108332
  if (!installed || !selected)
107450
108333
  throw new Error("Updated command is unavailable");
107451
- const selectedPath = realpathSync11(selected);
107452
- if (selectedPath !== realpathSync11(installed))
108334
+ const selectedPath = realpathSync12(selected);
108335
+ if (selectedPath !== realpathSync12(installed))
107453
108336
  throw new Error("Updated command is shadowed on PATH");
107454
108337
  const version2 = (await readUpdateCommand([selectedPath, "--version"])).trim();
107455
108338
  if (!new RegExp(SEMVER_PATTERN).test(version2))
@@ -107580,14 +108463,14 @@ function requireHttpUrl(value) {
107580
108463
  }
107581
108464
  function promptLine(question) {
107582
108465
  const rl = createInterface({ input: process.stdin, output: process.stdout });
107583
- return new Promise((resolve22) => {
108466
+ return new Promise((resolve23) => {
107584
108467
  let settled = false;
107585
108468
  const finish = (answer) => {
107586
108469
  if (settled)
107587
108470
  return;
107588
108471
  settled = true;
107589
108472
  rl.close();
107590
- resolve22(answer);
108473
+ resolve23(answer);
107591
108474
  };
107592
108475
  rl.once("SIGINT", () => finish(null));
107593
108476
  rl.once("close", () => finish(null));
@@ -107707,7 +108590,7 @@ async function handleRun(name, args2, options, separatorBeforeSkill) {
107707
108590
  if (!Number.isFinite(timeoutMs) || timeoutMs < 1 || timeoutMs > 1200000 || !Number.isFinite(intervalMs) || intervalMs < 100 || intervalMs > 60000)
107708
108591
  throw new Error("Invalid cloud polling bounds");
107709
108592
  const client3 = await CloudExecutionClient.configured();
107710
- const admitted = await client3.submit(slug2, version2, input2, options.idempotencyKey ?? randomUUID9(), selected?.selection);
108593
+ const admitted = await client3.submit(slug2, version2, input2, options.idempotencyKey ?? randomUUID10(), selected?.selection);
107711
108594
  if (options.wait)
107712
108595
  console.error(JSON.stringify({ executionId: admitted.id, status: admitted.status }));
107713
108596
  const result3 = options.wait ? await client3.wait(admitted.id, { timeoutMs, intervalMs }) : admitted;
@@ -107764,10 +108647,10 @@ async function handleRun(name, args2, options, separatorBeforeSkill) {
107764
108647
  try {
107765
108648
  parsePollingOptions(options);
107766
108649
  inputFiles = (options.file ?? []).map((path) => {
107767
- const info = lstatSync27(path);
108650
+ const info = lstatSync28(path);
107768
108651
  if (!info.isFile() || info.size > 20 * 1024 * 1024)
107769
108652
  throw new Error("Input must be a regular file no larger than 20 MiB");
107770
- return { name: basename13(path), bytes: new Uint8Array(readFileSync37(path)) };
108653
+ return { name: basename14(path), bytes: new Uint8Array(readFileSync38(path)) };
107771
108654
  });
107772
108655
  describeRemoteFiles(inputFiles);
107773
108656
  client2 = new RemoteSkillsClient(routing.apiKey, routing.apiOrigin);
@@ -108145,7 +109028,7 @@ async function handleExportsDownload(runId2, options) {
108145
109028
  const canonicalSkill = typeof remoteRun.skill === "string" ? remoteRun.skill : "remote";
108146
109029
  const requestedSkill = typeof remoteRun.requestedSlug === "string" && remoteRun.requestedSlug.trim() ? remoteRun.requestedSlug : canonicalSkill;
108147
109030
  const exportDir = getRunExportDir(runId2, requestedSkill);
108148
- mkdirSync24(exportDir, { recursive: true });
109031
+ mkdirSync25(exportDir, { recursive: true });
108149
109032
  const downloaded = [];
108150
109033
  for (const artifact of artifacts) {
108151
109034
  const artifactId = String(artifact.id || "");
@@ -108153,9 +109036,9 @@ async function handleExportsDownload(runId2, options) {
108153
109036
  continue;
108154
109037
  const verified = await client2.getVerifiedRunArtifact(runId2, artifactId);
108155
109038
  const relativePath = safeArtifactRelativePath(typeof artifact.relativePath === "string" ? artifact.relativePath : artifact.fileName, String(artifact.fileName || artifactId));
108156
- const outputPath = join54(exportDir, relativePath);
109039
+ const outputPath = join55(exportDir, relativePath);
108157
109040
  ensureSafeExportParent(exportDir, relativePath);
108158
- writeFileSync23(outputPath, verified.bytes, { flag: "wx", mode: 384 });
109041
+ writeFileSync24(outputPath, verified.bytes, { flag: "wx", mode: 384 });
108159
109042
  downloaded.push({ id: artifactId, path: outputPath, byteSize: verified.byteSize });
108160
109043
  }
108161
109044
  const payload = {
@@ -108192,14 +109075,14 @@ function ensureSafeExportParent(root2, relativePath) {
108192
109075
  let parent = root2;
108193
109076
  for (const part of ["", ...parts]) {
108194
109077
  if (part)
108195
- parent = join54(parent, part);
109078
+ parent = join55(parent, part);
108196
109079
  try {
108197
- if (!lstatSync27(parent).isDirectory() || lstatSync27(parent).isSymbolicLink())
109080
+ if (!lstatSync28(parent).isDirectory() || lstatSync28(parent).isSymbolicLink())
108198
109081
  throw new Error("Unsafe artifact directory");
108199
109082
  } catch (error61) {
108200
109083
  if (error61.code !== "ENOENT")
108201
109084
  throw error61;
108202
- mkdirSync24(parent, { mode: 448 });
109085
+ mkdirSync25(parent, { mode: 448 });
108203
109086
  }
108204
109087
  }
108205
109088
  }
@@ -108232,7 +109115,7 @@ async function pollRemoteRun(client2, runId2, options) {
108232
109115
  const remaining = deadline - Date.now();
108233
109116
  if (remaining <= 0)
108234
109117
  break;
108235
- await new Promise((resolve22) => setTimeout(resolve22, Math.min(options.intervalMs, remaining)));
109118
+ await new Promise((resolve23) => setTimeout(resolve23, Math.min(options.intervalMs, remaining)));
108236
109119
  }
108237
109120
  return {
108238
109121
  run: current ?? { id: runId2, status: "queued" },
@@ -108403,6 +109286,7 @@ var init_runtime = __esm(() => {
108403
109286
  init_remote_client();
108404
109287
  init_remote_account2();
108405
109288
  init_remote_files();
109289
+ init_runtime_copyfile();
108406
109290
  });
108407
109291
 
108408
109292
  // src/cli/commands/customer-verification.ts
@@ -108424,7 +109308,7 @@ async function readCode() {
108424
109308
  function promptCode() {
108425
109309
  const { stdin, stderr: output2 } = process;
108426
109310
  const { isRaw: wasRaw, readableFlowing: wasFlowing } = stdin;
108427
- return new Promise((resolve22) => {
109311
+ return new Promise((resolve23) => {
108428
109312
  let value = "", settled = false;
108429
109313
  const finish = (answer) => {
108430
109314
  if (settled)
@@ -108441,7 +109325,7 @@ function promptCode() {
108441
109325
  `);
108442
109326
  if (answer === null)
108443
109327
  process.exitCode = 130;
108444
- resolve22(answer);
109328
+ resolve23(answer);
108445
109329
  };
108446
109330
  const cancel = () => finish(null);
108447
109331
  const keypress = (text4, key) => {
@@ -108486,7 +109370,7 @@ var exports_recurring = {};
108486
109370
  __export(exports_recurring, {
108487
109371
  registerRecurringCommands: () => registerRecurringCommands
108488
109372
  });
108489
- import { constants as constants19, closeSync as closeSync22, fstatSync as fstatSync22, lstatSync as lstatSync28, openSync as openSync22, readSync as readSync16 } from "fs";
109373
+ import { constants as constants19, closeSync as closeSync22, fstatSync as fstatSync22, lstatSync as lstatSync29, openSync as openSync22, readSync as readSync16 } from "fs";
108490
109374
  import { createInterface as createInterface2 } from "readline";
108491
109375
  function readRequest(path) {
108492
109376
  const fd = openSync22(path, constants19.O_RDONLY | constants19.O_NOFOLLOW | constants19.O_NONBLOCK);
@@ -108502,7 +109386,7 @@ function readRequest(path) {
108502
109386
  break;
108503
109387
  length += n2;
108504
109388
  }
108505
- const after2 = fstatSync22(fd), current = lstatSync28(path);
109389
+ const after2 = fstatSync22(fd), current = lstatSync29(path);
108506
109390
  if (length !== before2.size || after2.size !== before2.size || before2.mtimeMs !== after2.mtimeMs || before2.ctimeMs !== after2.ctimeMs || current.dev !== before2.dev || current.ino !== before2.ino || !current.isFile() || current.isSymbolicLink())
108507
109391
  throw new RecurringInputError;
108508
109392
  return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, length)));
@@ -108513,10 +109397,10 @@ function readRequest(path) {
108513
109397
  }
108514
109398
  }
108515
109399
  async function acceptAtTerminal(draft) {
108516
- await new Promise((resolve22, reject2) => process.stderr.write(`${jsonText(draft)}
109400
+ await new Promise((resolve23, reject2) => process.stderr.write(`${jsonText(draft)}
108517
109401
  This grants an additional recurring budget. Each occurrence reprices within the approved ceilings. Revocation prevents new authority; an already-authorized attempt may finish, and cancellation is separate.
108518
- `, (error61) => error61 ? reject2(error61) : resolve22()));
108519
- return new Promise((resolve22) => {
109402
+ `, (error61) => error61 ? reject2(error61) : resolve23()));
109403
+ return new Promise((resolve23) => {
108520
109404
  const rl = createInterface2({ input: process.stdin, output: process.stderr });
108521
109405
  let settled = false;
108522
109406
  const finish = (accepted) => {
@@ -108527,7 +109411,7 @@ This grants an additional recurring budget. Each occurrence reprices within the
108527
109411
  rl.close();
108528
109412
  if (!accepted)
108529
109413
  process.exitCode = 130;
108530
- resolve22(accepted);
109414
+ resolve23(accepted);
108531
109415
  };
108532
109416
  const timer = setTimeout(() => finish(false), 5 * 60 * 1000);
108533
109417
  rl.once("SIGINT", () => finish(false));
@@ -108786,9 +109670,9 @@ var init_completion = __esm(() => {
108786
109670
  });
108787
109671
 
108788
109672
  // src/lib/portable-snapshot-filter.ts
108789
- import { readdirSync as readdirSync20, statSync as statSync18 } from "fs";
109673
+ import { readdirSync as readdirSync21, statSync as statSync18 } from "fs";
108790
109674
  import { homedir as homedir11 } from "os";
108791
- import { join as join55, sep as sep9 } from "path";
109675
+ import { join as join56, sep as sep10 } from "path";
108792
109676
  function isExcludedSkillFileName(fileName) {
108793
109677
  if (EXCLUDE_FILE_NAMES.has(fileName)) {
108794
109678
  return true;
@@ -108811,27 +109695,27 @@ function isPortableWithinSkill(relativeParts) {
108811
109695
  function homePathFor(definition, homesRoot) {
108812
109696
  const home = homesRoot ?? homedir11();
108813
109697
  if (definition.subClass === "skills" || definition.subClass === "custom") {
108814
- return join55(skillsDataRootForHome(home), definition.name);
109698
+ return join56(skillsDataRootForHome(home), definition.name);
108815
109699
  }
108816
109700
  if (definition.agent === "opencode") {
108817
- return join55(home, ".config", "opencode", "skills");
109701
+ return join56(home, ".config", "opencode", "skills");
108818
109702
  }
108819
- return join55(home, `.${definition.agent}`, "skills");
109703
+ return join56(home, `.${definition.agent}`, "skills");
108820
109704
  }
108821
109705
  function destinationFor(definition, stationId, relativePath) {
108822
- const category = definition.subClass === "agent-homes" ? join55("agent-homes", definition.agent ?? "") : definition.name;
108823
- return join55("resources", stationId, "skills", category, ...relativePath.split(sep9));
109706
+ const category = definition.subClass === "agent-homes" ? join56("agent-homes", definition.agent ?? "") : definition.name;
109707
+ return join56("resources", stationId, "skills", category, ...relativePath.split(sep10));
108824
109708
  }
108825
109709
  function walkEntries(absoluteRoot) {
108826
109710
  let entries;
108827
109711
  try {
108828
- entries = readdirSync20(absoluteRoot, { withFileTypes: true });
109712
+ entries = readdirSync21(absoluteRoot, { withFileTypes: true });
108829
109713
  } catch {
108830
109714
  return [];
108831
109715
  }
108832
109716
  const output2 = [];
108833
109717
  for (const entry of entries) {
108834
- const childFull = join55(absoluteRoot, entry.name);
109718
+ const childFull = join56(absoluteRoot, entry.name);
108835
109719
  if (entry.isSymbolicLink()) {
108836
109720
  output2.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
108837
109721
  continue;
@@ -108842,7 +109726,7 @@ function walkEntries(absoluteRoot) {
108842
109726
  }
108843
109727
  const nested = walkEntries(childFull);
108844
109728
  for (const item of nested) {
108845
- output2.push({ ...item, relativePath: join55(entry.name, item.relativePath) });
109729
+ output2.push({ ...item, relativePath: join56(entry.name, item.relativePath) });
108846
109730
  }
108847
109731
  continue;
108848
109732
  }
@@ -108935,22 +109819,22 @@ var init_portable_snapshot_filter = __esm(() => {
108935
109819
  });
108936
109820
 
108937
109821
  // src/lib/station-snapshot.ts
108938
- import { createHash as createHash27 } from "crypto";
109822
+ import { createHash as createHash28 } from "crypto";
108939
109823
  import {
108940
- copyFileSync,
108941
- mkdirSync as mkdirSync25,
108942
- readFileSync as readFileSync38,
109824
+ copyFileSync as copyFileSync2,
109825
+ mkdirSync as mkdirSync26,
109826
+ readFileSync as readFileSync39,
108943
109827
  statSync as statSync19,
108944
- writeFileSync as writeFileSync24
109828
+ writeFileSync as writeFileSync25
108945
109829
  } from "fs";
108946
- import { dirname as dirname30, isAbsolute as isAbsolute23, relative as relative8, resolve as resolve22, sep as sep10 } from "path";
109830
+ import { dirname as dirname31, isAbsolute as isAbsolute24, relative as relative9, resolve as resolve23, sep as sep11 } from "path";
108947
109831
  function validateStationId(stationId) {
108948
109832
  if (!/^[a-z0-9-]+$/.test(stationId)) {
108949
109833
  throw new StationSnapshotError("INVALID_STATION", `station id must be a slug, got: ${stationId}`);
108950
109834
  }
108951
109835
  }
108952
109836
  function sha256File(filePath) {
108953
- return createHash27("sha256").update(readFileSync38(filePath)).digest("hex");
109837
+ return createHash28("sha256").update(readFileSync39(filePath)).digest("hex");
108954
109838
  }
108955
109839
  function scanHome(definition, homesRoot) {
108956
109840
  const homePath = homePathFor(definition, homesRoot);
@@ -108958,7 +109842,7 @@ function scanHome(definition, homesRoot) {
108958
109842
  const portable = [];
108959
109843
  const skipped = [];
108960
109844
  for (const entry of entries) {
108961
- const relativeParts = entry.relativePath.split(sep10);
109845
+ const relativeParts = entry.relativePath.split(sep11);
108962
109846
  const fileName = relativeParts[relativeParts.length - 1];
108963
109847
  if (entry.kind === "symlink") {
108964
109848
  skipped.push({ relativePath: entry.relativePath, reason: "symlink" });
@@ -109021,7 +109905,7 @@ function humanHomes(scanned) {
109021
109905
  }));
109022
109906
  }
109023
109907
  function writeStationSnapshot(options) {
109024
- const repoRoot = resolve22(options.repoRoot ?? process.cwd());
109908
+ const repoRoot = resolve23(options.repoRoot ?? process.cwd());
109025
109909
  const { scanned, plans, totalBytes } = planStationSnapshot(options);
109026
109910
  const manifestFiles = plans.map((plan) => ({
109027
109911
  relativePath: plan.source.relativePath,
@@ -109047,9 +109931,9 @@ function writeStationSnapshot(options) {
109047
109931
  const conflicts = [];
109048
109932
  const untouched = [];
109049
109933
  for (const plan of plans) {
109050
- const destination = resolve22(repoRoot, plan.destination);
109051
- const destinationRelative = relative8(repoRoot, destination);
109052
- if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep10) || isAbsolute23(destinationRelative)) {
109934
+ const destination = resolve23(repoRoot, plan.destination);
109935
+ const destinationRelative = relative9(repoRoot, destination);
109936
+ if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep11) || isAbsolute24(destinationRelative)) {
109053
109937
  throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
109054
109938
  }
109055
109939
  let existingDigest = null;
@@ -109070,9 +109954,9 @@ function writeStationSnapshot(options) {
109070
109954
  }
109071
109955
  let written = 0;
109072
109956
  for (const plan of untouched) {
109073
- const destination = resolve22(repoRoot, plan.destination);
109074
- mkdirSync25(dirname30(destination), { recursive: true });
109075
- copyFileSync(plan.source.fullPath, destination);
109957
+ const destination = resolve23(repoRoot, plan.destination);
109958
+ mkdirSync26(dirname31(destination), { recursive: true });
109959
+ copyFileSync2(plan.source.fullPath, destination);
109076
109960
  written += 1;
109077
109961
  }
109078
109962
  const unchanged3 = plans.length - untouched.length;
@@ -109089,9 +109973,9 @@ function writeStationSnapshot(options) {
109089
109973
  },
109090
109974
  files: manifestFiles
109091
109975
  };
109092
- const manifestPath = resolve22(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
109093
- mkdirSync25(dirname30(manifestPath), { recursive: true });
109094
- writeFileSync24(manifestPath, `${JSON.stringify(manifest, null, 2)}
109976
+ const manifestPath = resolve23(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
109977
+ mkdirSync26(dirname31(manifestPath), { recursive: true });
109978
+ writeFileSync25(manifestPath, `${JSON.stringify(manifest, null, 2)}
109095
109979
  `);
109096
109980
  return {
109097
109981
  ...base3,
@@ -109122,8 +110006,8 @@ var exports_create_sync_config = {};
109122
110006
  __export(exports_create_sync_config, {
109123
110007
  registerCreateSync: () => registerCreateSync
109124
110008
  });
109125
- import { existsSync as existsSync33 } from "fs";
109126
- import { join as join56 } from "path";
110009
+ import { existsSync as existsSync34 } from "fs";
110010
+ import { join as join57 } from "path";
109127
110011
  function registerCreateSync(parent) {
109128
110012
  const configCmd = parent.command("config").description("Manage skills configuration");
109129
110013
  configCmd.command("show", { isDefault: true }).option("--json", "Output as JSON", false).description("Show current merged configuration").action((options) => {
@@ -109203,13 +110087,13 @@ function registerCreateSync(parent) {
109203
110087
  const pp = getConfigPath("project");
109204
110088
  if (options.json) {
109205
110089
  console.log(JSON.stringify({
109206
- global: { path: gp, exists: existsSync33(gp) },
109207
- project: { path: pp, exists: existsSync33(pp) }
110090
+ global: { path: gp, exists: existsSync34(gp) },
110091
+ project: { path: pp, exists: existsSync34(pp) }
109208
110092
  }, null, 2));
109209
110093
  return;
109210
110094
  }
109211
- console.log(`${source_default.cyan("global")}: ${gp}${existsSync33(gp) ? source_default.green(" (exists)") : source_default.dim(" (not found)")}`);
109212
- console.log(`${source_default.cyan("project")}: ${pp}${existsSync33(pp) ? source_default.green(" (exists)") : source_default.dim(" (not found)")}`);
110095
+ console.log(`${source_default.cyan("global")}: ${gp}${existsSync34(gp) ? source_default.green(" (exists)") : source_default.dim(" (not found)")}`);
110096
+ console.log(`${source_default.cyan("project")}: ${pp}${existsSync34(pp) ? source_default.green(" (exists)") : source_default.dim(" (not found)")}`);
109213
110097
  });
109214
110098
  parent.command("create").argument("<name>", "Skill name (e.g. my-tool)").option("--category <category>", "Skill category", "Development Tools").option("--description <description>", "Short description of what the skill does").option("--kind <kind>", "Skill class: executable or instruction", "executable").option("--tags <tags>", "Comma-separated tags (e.g. api,testing,automation)").option("--global", "Deprecated; custom skills are always global", false).option("--json", "Output result as JSON", false).description("Scaffold a new custom skill directory").action((name, options) => handleCreate(name, options));
109215
110099
  parent.command("sync").alias("render").option("--selection-profile <id>", "Consume the exact versions selected by an API profile").option("--profile <id>", "Selection profile (alias for --selection-profile; use before sync for credential profile)").option("--project", "Record exact selected versions in this project's lockfile", false).option("--no-station-report", "Verify and activate the selected profile without reporting station state").argument("[names...]", "Skills to sync (default: every skill in this machine's corpus)").option("--for <agent>", `Target one agent (${SYNC_AGENTS.join(", ")}, or all)`, "all").option("--all", "Sync every corpus skill (the default)", false).option("--source <path>", "Canonical corpus source: a directory of skill folders, or a package root with skills/ (overrides $SKILLS_SOURCE)").option("--dry-run", "Show what would be written without touching any agent folder", false).option("--force", "Adopt an unmanaged skill that already has SKILL.md; other unmarked directories are never overwritten", false).option("--check", "Home drift census (missing-from-home / stray-in-home / diverged); exits non-zero on drift, writes nothing", false).option("--adopt", "Unmarked-home adoption mode: hash unmarked home skills against the corpus; exact matches are marked (dry-run by default)", false).option("--prune", "Prune mode: list (or with --apply, remove) marked home skill dirs that have no canonical corpus entry", false).option("--apply", "Write adoption markers / conflicts ledger, or perform prune removals", false).option("--json", "Output as JSON", false).option("--station <id>", "Per-station snapshot mode: snapshot the installed skill homes into resources/<station>/skills with a v3 sync-manifest (dry-run by default; --populate writes)").option("--populate", "Write the per-station snapshot (station mode; the default is dry-run)", false).option("--repo-root <path>", "Station snapshot destination repo root (default: cwd)").option("--homes-root <dir>", "Build the station snapshot from a staged mirror of the skill homes instead of this machine's $HOME").description("Sync an API selection profile into the Skills cache; native migration flags remain available for older installations").action((names, options) => handleSync(names, options));
@@ -109233,7 +110117,7 @@ function handleCreate(name, options) {
109233
110117
  console.log(source_default.green(`\u2713 Created custom skill '${result2.name}' at ${result2.path}`));
109234
110118
  console.log(source_default.dim(` Category: ${result2.manifest.category}`));
109235
110119
  console.log(source_default.dim(` Tags: ${result2.manifest.tags?.join(", ")}`));
109236
- console.log(` ${source_default.cyan("Edit:")} ${join56(result2.path, options.kind === "instruction" ? "SKILL.md" : "src/index.ts")}`);
110120
+ console.log(` ${source_default.cyan("Edit:")} ${join57(result2.path, options.kind === "instruction" ? "SKILL.md" : "src/index.ts")}`);
109237
110121
  console.log(` ${source_default.cyan("Prepare:")} skills prepare ${result2.name} --version <new-semver>`);
109238
110122
  }
109239
110123
  } catch (error61) {
@@ -109569,14 +110453,14 @@ var init_codex_native_config = () => {};
109569
110453
 
109570
110454
  // src/lib/codex-hook-rpc.ts
109571
110455
  import { execFile, spawn as spawn3 } from "child_process";
109572
- import { realpathSync as realpathSync12, statSync as statSync20 } from "fs";
109573
- import { isAbsolute as isAbsolute24 } from "path";
110456
+ import { realpathSync as realpathSync13, statSync as statSync20 } from "fs";
110457
+ import { isAbsolute as isAbsolute25 } from "path";
109574
110458
  import { promisify } from "util";
109575
110459
  async function connectCodexHookRpc(options) {
109576
- const found = isAbsolute24(options.command) ? options.command : Bun.which(options.command, { PATH: process.env.PATH });
110460
+ const found = isAbsolute25(options.command) ? options.command : Bun.which(options.command, { PATH: process.env.PATH });
109577
110461
  if (!found)
109578
110462
  throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE: install Codex or pass --codex-command");
109579
- const binary = realpathSync12(found), stat3 = statSync20(binary);
110463
+ const binary = realpathSync13(found), stat3 = statSync20(binary);
109580
110464
  if (!stat3.isFile() || (stat3.mode & 18) !== 0)
109581
110465
  throw new Error("CODEX_HOOK_TRUST_UNSAFE_EXECUTABLE");
109582
110466
  let version2;
@@ -109589,7 +110473,7 @@ async function connectCodexHookRpc(options) {
109589
110473
  if (!SUPPORTED_CODEX_HOOK_VERSIONS.includes(version2))
109590
110474
  throw new Error("CODEX_HOOK_TRUST_NATIVE_UNSUPPORTED_VERSION");
109591
110475
  const current = statSync20(binary);
109592
- if (realpathSync12(found) !== binary || ["dev", "ino", "size", "mtimeMs", "ctimeMs"].some((k) => stat3[k] !== current[k]))
110476
+ if (realpathSync13(found) !== binary || ["dev", "ino", "size", "mtimeMs", "ctimeMs"].some((k) => stat3[k] !== current[k]))
109593
110477
  throw new Error("CODEX_HOOK_TRUST_NATIVE_EXECUTABLE_CHANGED");
109594
110478
  const env3 = { ...process.env, HOME: options.home, NO_COLOR: "1" };
109595
110479
  if (options.codexHome === undefined)
@@ -109664,13 +110548,13 @@ async function connectCodexHookRpc(options) {
109664
110548
  request(method2, params) {
109665
110549
  if (stopped)
109666
110550
  return Promise.reject(new Error("CODEX_HOOK_TRUST_NATIVE_RPC_CLOSED"));
109667
- return new Promise((resolve23, reject2) => {
110551
+ return new Promise((resolve24, reject2) => {
109668
110552
  const id2 = nextId++;
109669
110553
  const timer = setTimeout(() => {
109670
110554
  fail5();
109671
110555
  child.kill();
109672
110556
  }, 20000);
109673
- pending.set(id2, { resolve: resolve23, reject: reject2, timer });
110557
+ pending.set(id2, { resolve: resolve24, reject: reject2, timer });
109674
110558
  child.stdin.write(JSON.stringify({ id: id2, method: method2, params }) + `
109675
110559
  `);
109676
110560
  });
@@ -109680,14 +110564,14 @@ async function connectCodexHookRpc(options) {
109680
110564
  child.stdin.end();
109681
110565
  if (child.exitCode !== null || child.signalCode !== null)
109682
110566
  return;
109683
- await new Promise((resolve23) => {
110567
+ await new Promise((resolve24) => {
109684
110568
  const timer = setTimeout(() => {
109685
110569
  child.kill();
109686
- resolve23();
110570
+ resolve24();
109687
110571
  }, 1000);
109688
110572
  child.once("exit", () => {
109689
110573
  clearTimeout(timer);
109690
- resolve23();
110574
+ resolve24();
109691
110575
  });
109692
110576
  });
109693
110577
  }
@@ -109708,21 +110592,21 @@ var init_codex_hook_rpc = __esm(() => {
109708
110592
  });
109709
110593
 
109710
110594
  // src/lib/codex-hook-trust-identity.ts
109711
- import { realpathSync as realpathSync13 } from "fs";
109712
- import { dirname as dirname31, isAbsolute as isAbsolute25, join as join57 } from "path";
110595
+ import { realpathSync as realpathSync14 } from "fs";
110596
+ import { dirname as dirname32, isAbsolute as isAbsolute26, join as join58 } from "path";
109713
110597
  import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
109714
110598
  function inspectRecordedSkillsCli(command, expected) {
109715
- need4(isAbsolute25(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
109716
- const resolved = realpathSync13(command);
110599
+ need4(isAbsolute26(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
110600
+ const resolved = realpathSync14(command);
109717
110601
  const cli = snapshot(resolved, false, { readOnlyPackage: true });
109718
- const manifest = snapshot(join57(dirname31(dirname31(resolved)), "package.json"), false, { readOnlyPackage: true });
110602
+ const manifest = snapshot(join58(dirname32(dirname32(resolved)), "package.json"), false, { readOnlyPackage: true });
109719
110603
  const pkg = JSON.parse(manifest.text);
109720
- need4(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join57(dirname31(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
110604
+ need4(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join58(dirname32(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
109721
110605
  need4((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
109722
110606
  const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
109723
110607
  need4(isDeepStrictEqual11(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
109724
110608
  return { receipt, recheck() {
109725
- need4(realpathSync13(command) === resolved, "SKILLS_COMMAND_CHANGED");
110609
+ need4(realpathSync14(command) === resolved, "SKILLS_COMMAND_CHANGED");
109726
110610
  unchanged2(cli);
109727
110611
  unchanged2(manifest);
109728
110612
  } };
@@ -109730,22 +110614,22 @@ function inspectRecordedSkillsCli(command, expected) {
109730
110614
  function bindSkillsCli(command, reviewed) {
109731
110615
  const normal = Bun.which("skills", { PATH: process.env.PATH });
109732
110616
  need4(normal, "SKILLS_COMMAND_UNAVAILABLE");
109733
- const resolved = realpathSync13(normal);
109734
- const bound = isAbsolute25(command) ? command : command === "skills" ? normal : undefined;
109735
- need4(bound && realpathSync13(bound) === resolved, "SKILLS_COMMAND_MISMATCH");
110617
+ const resolved = realpathSync14(normal);
110618
+ const bound = isAbsolute26(command) ? command : command === "skills" ? normal : undefined;
110619
+ need4(bound && realpathSync14(bound) === resolved, "SKILLS_COMMAND_MISMATCH");
109736
110620
  const entrypoint = reviewed?.path ?? process.argv[1];
109737
- need4(entrypoint && realpathSync13(entrypoint) === resolved, "SKILLS_ENTRYPOINT_MISMATCH");
110621
+ need4(entrypoint && realpathSync14(entrypoint) === resolved, "SKILLS_ENTRYPOINT_MISMATCH");
109738
110622
  const cli = snapshot(resolved, false, { readOnlyPackage: true });
109739
- const manifest = snapshot(join57(dirname31(dirname31(resolved)), "package.json"), false, { readOnlyPackage: true });
110623
+ const manifest = snapshot(join58(dirname32(dirname32(resolved)), "package.json"), false, { readOnlyPackage: true });
109740
110624
  const pkg = JSON.parse(manifest.text);
109741
- need4(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join57(dirname31(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
110625
+ need4(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join58(dirname32(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
109742
110626
  if (reviewed)
109743
110627
  need4(/^[a-f0-9]{64}$/.test(reviewed.sha256) && reviewed.sha256 === cli.sha256 && reviewed.version === pkg.version, "SKILLS_RELEASE_MISMATCH");
109744
110628
  need4((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
109745
110629
  return {
109746
110630
  receipt: { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 },
109747
110631
  recheck() {
109748
- need4(Bun.which("skills", { PATH: process.env.PATH }) === normal && realpathSync13(normal) === resolved && realpathSync13(bound) === resolved && realpathSync13(entrypoint) === resolved, "SKILLS_COMMAND_CHANGED");
110632
+ need4(Bun.which("skills", { PATH: process.env.PATH }) === normal && realpathSync14(normal) === resolved && realpathSync14(bound) === resolved && realpathSync14(entrypoint) === resolved, "SKILLS_COMMAND_CHANGED");
109749
110633
  unchanged2(cli);
109750
110634
  unchanged2(manifest);
109751
110635
  }
@@ -109779,23 +110663,23 @@ var init_codex_hook_policy_recovery = __esm(() => {
109779
110663
  });
109780
110664
 
109781
110665
  // src/lib/agent-codex-trust.ts
109782
- import { lstatSync as lstatSync29, mkdirSync as mkdirSync26, readdirSync as readdirSync21, readFileSync as readFileSync39, realpathSync as realpathSync14, statSync as statSync21 } from "fs";
109783
- import { createHash as createHash28, randomUUID as randomUUID10 } from "crypto";
110666
+ import { lstatSync as lstatSync30, mkdirSync as mkdirSync27, readdirSync as readdirSync22, readFileSync as readFileSync40, realpathSync as realpathSync15, statSync as statSync21 } from "fs";
110667
+ import { createHash as createHash29, randomUUID as randomUUID11 } from "crypto";
109784
110668
  import { execFileSync } from "child_process";
109785
- import { dirname as dirname32, isAbsolute as isAbsolute26, join as join58, resolve as resolve23 } from "path";
110669
+ import { dirname as dirname33, isAbsolute as isAbsolute27, join as join59, resolve as resolve24 } from "path";
109786
110670
  import { homedir as homedir12 } from "os";
109787
110671
  import { isDeepStrictEqual as isDeepStrictEqual13 } from "util";
109788
110672
  function verifyReleaseArtifact(proof, skillsCli) {
109789
- need4(isAbsolute26(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
109790
- const tarPath = realpathSync14(proof.packageTarPath);
110673
+ need4(isAbsolute27(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
110674
+ const tarPath = realpathSync15(proof.packageTarPath);
109791
110675
  need4(statSync21(tarPath).isFile(), "RECONCILE_RELEASE_PROVENANCE_PATH");
109792
- const tarBytes = readFileSync39(tarPath);
109793
- need4(tarBytes.byteLength === proof.packageTarBytes && createHash28("sha256").update(tarBytes).digest("hex") === proof.packageTarSha256, "RECONCILE_RELEASE_ARTIFACT_CHANGED");
109794
- const packageRoot = dirname32(dirname32(realpathSync14(skillsCli.receipt.path)));
109795
- const manifest = readFileSync39(join58(packageRoot, "package.json"));
109796
- need4(createHash28("sha256").update(manifest).digest("hex") === skillsCli.receipt.manifestSha256 && skillsCli.receipt.manifestSha256 === proof.manifestSha256, "RECONCILE_RELEASE_MANIFEST_CHANGED");
109797
- need4(createHash28("sha256").update(readFileSync39(skillsCli.receipt.path)).digest("hex") === proof.executableSha256 && skillsCli.receipt.sha256 === proof.executableSha256, "RECONCILE_RELEASE_EXECUTABLE_CHANGED");
109798
- const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync39(skillsCli.receipt.path)]];
110676
+ const tarBytes = readFileSync40(tarPath);
110677
+ need4(tarBytes.byteLength === proof.packageTarBytes && createHash29("sha256").update(tarBytes).digest("hex") === proof.packageTarSha256, "RECONCILE_RELEASE_ARTIFACT_CHANGED");
110678
+ const packageRoot = dirname33(dirname33(realpathSync15(skillsCli.receipt.path)));
110679
+ const manifest = readFileSync40(join59(packageRoot, "package.json"));
110680
+ need4(createHash29("sha256").update(manifest).digest("hex") === skillsCli.receipt.manifestSha256 && skillsCli.receipt.manifestSha256 === proof.manifestSha256, "RECONCILE_RELEASE_MANIFEST_CHANGED");
110681
+ need4(createHash29("sha256").update(readFileSync40(skillsCli.receipt.path)).digest("hex") === proof.executableSha256 && skillsCli.receipt.sha256 === proof.executableSha256, "RECONCILE_RELEASE_EXECUTABLE_CHANGED");
110682
+ const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync40(skillsCli.receipt.path)]];
109799
110683
  for (const [member, expected] of members2) {
109800
110684
  let actual;
109801
110685
  try {
@@ -109809,26 +110693,26 @@ function verifyReleaseArtifact(proof, skillsCli) {
109809
110693
  async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc) {
109810
110694
  let rpc;
109811
110695
  try {
109812
- const home = resolve23(options.home ?? homedir12()), dataDir3 = resolve23(options.dataDir ?? getDataDirReadOnly());
109813
- const parent = resolve23(join58(dataDir3, "native-hook-trust")), journal = resolve23(options.journal);
110696
+ const home = resolve24(options.home ?? homedir12()), dataDir3 = resolve24(options.dataDir ?? getDataDirReadOnly());
110697
+ const parent = resolve24(join59(dataDir3, "native-hook-trust")), journal = resolve24(options.journal);
109814
110698
  need4(journal.startsWith(parent + "/") && /^[a-f0-9-]{36}$/.test(journal.slice(parent.length + 1)), "RECONCILE_JOURNAL_PATH");
109815
- const dir = lstatSync29(journal, { throwIfNoEntry: false });
110699
+ const dir = lstatSync30(journal, { throwIfNoEntry: false });
109816
110700
  need4(!!dir && dir.isDirectory() && !dir.isSymbolicLink() && dir.uid === process.getuid() && (dir.mode & 511) === 448, "UNSAFE_JOURNAL");
109817
- need4(!lstatSync29(join58(journal, "receipt.json"), { throwIfNoEntry: false }), "RECONCILE_ALREADY_COMPLETE");
109818
- need4(lstatSync29(join58(journal, "stopped.json"), { throwIfNoEntry: false })?.isFile(), "RECONCILE_JOURNAL_INCOMPLETE");
109819
- const stoppedFile = snapshot(join58(journal, "stopped.json"), true);
109820
- const intentFile = snapshot(join58(journal, "intent.json"), true), intent = JSON.parse(intentFile.text);
109821
- const configBefore = snapshot(join58(journal, "config.before.toml"), true), hooksBefore = snapshot(join58(journal, "hooks.before.json"), true), policyBefore = snapshot(join58(journal, "policy.before.json"), true);
110701
+ need4(!lstatSync30(join59(journal, "receipt.json"), { throwIfNoEntry: false }), "RECONCILE_ALREADY_COMPLETE");
110702
+ need4(lstatSync30(join59(journal, "stopped.json"), { throwIfNoEntry: false })?.isFile(), "RECONCILE_JOURNAL_INCOMPLETE");
110703
+ const stoppedFile = snapshot(join59(journal, "stopped.json"), true);
110704
+ const intentFile = snapshot(join59(journal, "intent.json"), true), intent = JSON.parse(intentFile.text);
110705
+ const configBefore = snapshot(join59(journal, "config.before.toml"), true), hooksBefore = snapshot(join59(journal, "hooks.before.json"), true), policyBefore = snapshot(join59(journal, "policy.before.json"), true);
109822
110706
  const legacyIntent = intent.home === undefined && intent.configSha256 === undefined && intent.configVersion === undefined && intent.declarations === undefined && intent.admitted === undefined && intent.nativeHooks === undefined;
109823
110707
  need4(intent.version === 1 && /^[a-f0-9]{64}$/.test(intent.planDigest) && typeof intent.configPath === "string" && /^[a-f0-9]{64}$/.test(intent.policySha256) && /^[a-f0-9]{64}$/.test(intent.hooksSha256) && Array.isArray(intent.hooks) && intent.hooks.length > 0 && intent.hooks.length <= 100 && (legacyIntent || typeof intent.home === "string" && intent.home === home && /^[a-f0-9]{64}$/.test(intent.configSha256) && typeof intent.configVersion === "string" && Array.isArray(intent.declarations) && Array.isArray(intent.admitted) && Array.isArray(intent.nativeHooks)), "RECONCILE_INTENT_INVALID");
109824
- const declarations = intent.declarations ?? intent.hooks.map((hook) => ({ event: hook.event, key: `${join58(home, ".codex/hooks.json")}${hook.key.slice(hook.key.indexOf(".codex/hooks.json") + ".codex/hooks.json".length)}`, command: hook.command }));
110708
+ const declarations = intent.declarations ?? intent.hooks.map((hook) => ({ event: hook.event, key: `${join59(home, ".codex/hooks.json")}${hook.key.slice(hook.key.indexOf(".codex/hooks.json") + ".codex/hooks.json".length)}`, command: hook.command }));
109825
110709
  const admitted = intent.admitted ?? intent.hooks;
109826
110710
  const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
109827
110711
  const digestInput = { version: 1, home: intent.home ?? home, configPath: intent.configPath, ...intent.nativeConfigPath !== undefined ? { nativeConfigPath: intent.nativeConfigPath } : {}, configSha256, configVersion, policySha256: intent.policySha256, hooksSha256: intent.hooksSha256, skillsCli: intent.skillsCli, nativeVersion: intent.nativeVersion, declarations, admitted };
109828
110712
  need4(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
109829
110713
  need4(isDeepStrictEqual13(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
109830
110714
  need4(configBefore.sha256 === (intent.beforeSha256 ?? intent.configSha256) && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
109831
- const policyCurrent = snapshot(join58(dataDir3, "agent-policy.json"), true);
110715
+ const policyCurrent = snapshot(join59(dataDir3, "agent-policy.json"), true);
109832
110716
  const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
109833
110717
  const recovery = claudeRecovery ?? additionsRecovery;
109834
110718
  need4(!recovery || !options.supersedeBinding, "RECONCILE_RECOVERY_MODE_CONFLICT");
@@ -109845,17 +110729,17 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109845
110729
  if (supersededBinding || recovery) {
109846
110730
  need4(Array.isArray(intent.nativeHooks) && intent.nativeHooks.length > 0, "RECONCILE_NATIVE_INVENTORY_REQUIRED");
109847
110731
  const proof = options.releaseProof;
109848
- need4(!!proof && isAbsolute26(proof.packageTarPath) && /^[a-f0-9]{64}$/.test(proof.packageTarSha256) && Number.isInteger(proof.packageTarBytes) && proof.packageTarBytes > 0 && /^[a-f0-9]{64}$/.test(proof.manifestSha256) && /^[a-f0-9]{64}$/.test(proof.executableSha256), "RECONCILE_RELEASE_PROVENANCE_REQUIRED");
110732
+ need4(!!proof && isAbsolute27(proof.packageTarPath) && /^[a-f0-9]{64}$/.test(proof.packageTarSha256) && Number.isInteger(proof.packageTarBytes) && proof.packageTarBytes > 0 && /^[a-f0-9]{64}$/.test(proof.manifestSha256) && /^[a-f0-9]{64}$/.test(proof.executableSha256), "RECONCILE_RELEASE_PROVENANCE_REQUIRED");
109849
110733
  }
109850
110734
  const currentConfig = snapshot(intent.configPath, true);
109851
110735
  const policy = parseManagedSkillPolicy(policyCurrent.text), binding = policy.bridge;
109852
- const alias = binding.rootAliases?.find((item) => item.agent === "codex"), physicalRoot = alias?.target ?? join58(home, ".codex"), hooksPath = join58(physicalRoot, "hooks.json");
109853
- need4(resolve23(intent.configPath) === resolve23(join58(physicalRoot, "config.toml")), "RECONCILE_CONFIG_PATH_CHANGED");
110736
+ const alias = binding.rootAliases?.find((item) => item.agent === "codex"), physicalRoot = alias?.target ?? join59(home, ".codex"), hooksPath = join59(physicalRoot, "hooks.json");
110737
+ need4(resolve24(intent.configPath) === resolve24(join59(physicalRoot, "config.toml")), "RECONCILE_CONFIG_PATH_CHANGED");
109854
110738
  const nativeConfigPath = intent.nativeConfigPath ?? intent.configPath;
109855
- need4([join58(home, ".codex/config.toml"), join58(physicalRoot, "config.toml")].includes(nativeConfigPath), "RECONCILE_NATIVE_CONFIG_PATH_CHANGED");
109856
- const codexHome = nativeConfigPath !== intent.configPath ? undefined : dirname32(nativeConfigPath);
110739
+ need4([join59(home, ".codex/config.toml"), join59(physicalRoot, "config.toml")].includes(nativeConfigPath), "RECONCILE_NATIVE_CONFIG_PATH_CHANGED");
110740
+ const codexHome = nativeConfigPath !== intent.configPath ? undefined : dirname33(nativeConfigPath);
109857
110741
  const hooksCurrent = snapshot(hooksPath);
109858
- const additions = additionsRecovery ? reviewNativeHookAdditions({ path: additionsRecovery.path, intent: intentFile, policy: policyCurrent, configBefore, configCurrent: currentConfig, hooksBefore, hooksCurrent, nativeHooksPath: join58(dirname32(nativeConfigPath), "hooks.json"), journalHooks: intent.hooks, recordedNativeHooks: intent.nativeHooks }) : undefined;
110742
+ const additions = additionsRecovery ? reviewNativeHookAdditions({ path: additionsRecovery.path, intent: intentFile, policy: policyCurrent, configBefore, configCurrent: currentConfig, hooksBefore, hooksCurrent, nativeHooksPath: join59(dirname33(nativeConfigPath), "hooks.json"), journalHooks: intent.hooks, recordedNativeHooks: intent.nativeHooks }) : undefined;
109859
110743
  const managedHooks = [...intent.hooks, ...additions?.hooks ?? []];
109860
110744
  const managedKeys = managedHooks.map((h) => h.key);
109861
110745
  const witnessSame = codexTrustReconcileWitness(configBefore.text, managedKeys) === codexTrustReconcileWitness(currentConfig.text, managedKeys);
@@ -109919,7 +110803,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109919
110803
  need4(isDeepStrictEqual13(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
109920
110804
  }
109921
110805
  for (const wanted of intent.hooks) {
109922
- const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join58(home, ".codex/hooks.json"), hooksPath));
110806
+ const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join59(home, ".codex/hooks.json"), hooksPath));
109923
110807
  need4(matches2.length === 1, "RECONCILE_NATIVE_IDENTITY_CHANGED");
109924
110808
  const h = matches2[0];
109925
110809
  const trusted = h.trustStatus === "trusted";
@@ -109968,7 +110852,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109968
110852
  const receipt = { version: 1, status: preview2 ? "recovery-review-required" : supersededBinding ? "superseded" : "reconciled", reconciled: !preview2, supersededBinding, automaticRollback: false, journal, planDigest: intent.planDigest, nativeVersion: rpc.version, nativeStateVerified: !nativeTrustPending, nativeTrustPending, bindingRefreshRequired: !!configDiscoveryRecovery, nativeExecutionVerified: false, configSha256: currentConfig.sha256, hooksSha256: hooksCurrent.sha256, policySha256: policyBefore.sha256, policyBeforeSha256: policyBefore.sha256, policyCurrentSha256: policyCurrent.sha256, previousSkillsCli: intent.skillsCli, skillsCli: skillsCli.receipt, releaseProof: options.releaseProof, nativeInventoryScope: recordedNativeHooks ? "complete" : "declared-managed-hooks-only", unrelatedNativeHooksPreserved: !!recordedNativeHooks, unrelatedSettingsAndCommentsPreserved: true, ...recoveryPlan ? { recoveryPlan, recoveryPlanDigest } : {} };
109969
110853
  if (preview2)
109970
110854
  return receipt;
109971
- save(join58(journal, "receipt.json"), json2(receipt));
110855
+ save(join59(journal, "receipt.json"), json2(receipt));
109972
110856
  return receipt;
109973
110857
  } catch (error61) {
109974
110858
  const message = error61 instanceof Error && error61.message.startsWith("CODEX_HOOK_TRUST_") ? error61.message : "CODEX_HOOK_TRUST_RECONCILE_REFUSED";
@@ -109980,15 +110864,15 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109980
110864
  async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRpc) {
109981
110865
  let rpc, journal;
109982
110866
  try {
109983
- const home = resolve23(options.home ?? homedir12()), dataDir3 = resolve23(options.dataDir ?? getDataDirReadOnly());
110867
+ const home = resolve24(options.home ?? homedir12()), dataDir3 = resolve24(options.dataDir ?? getDataDirReadOnly());
109984
110868
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home });
109985
- const policyFile = snapshot(join58(dataDir3, "agent-policy.json"), true), policy = parseManagedSkillPolicy(policyFile.text);
110869
+ const policyFile = snapshot(join59(dataDir3, "agent-policy.json"), true), policy = parseManagedSkillPolicy(policyFile.text);
109986
110870
  const binding = policy.bridge, alias = binding.rootAliases?.find((item) => item.agent === "codex");
109987
110871
  const skillsCli = bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
109988
- const physicalRoot = alias?.target ?? join58(home, ".codex"), hooksPath = join58(physicalRoot, "hooks.json"), configPath = join58(physicalRoot, "config.toml");
109989
- need4(options.codexHome === undefined || isAbsolute26(options.codexHome) && [join58(home, ".codex"), physicalRoot].includes(options.codexHome), "NATIVE_HOME_NOT_ADMITTED");
110872
+ const physicalRoot = alias?.target ?? join59(home, ".codex"), hooksPath = join59(physicalRoot, "hooks.json"), configPath = join59(physicalRoot, "config.toml");
110873
+ need4(options.codexHome === undefined || isAbsolute27(options.codexHome) && [join59(home, ".codex"), physicalRoot].includes(options.codexHome), "NATIVE_HOME_NOT_ADMITTED");
109990
110874
  const codexHome = options.codexHome === undefined ? undefined : physicalRoot;
109991
- const nativeConfigPath = join58(codexHome ?? join58(home, ".codex"), "config.toml");
110875
+ const nativeConfigPath = join59(codexHome ?? join59(home, ".codex"), "config.toml");
109992
110876
  const hooksFile = snapshot(hooksPath), configFile = snapshot(configPath, true), original = structure3(configFile.text);
109993
110877
  const declarations = JSON.parse(hooksFile.text).hooks, expected = [];
109994
110878
  for (const [event, [nativeEvent, label]] of Object.entries(eventNames)) {
@@ -109997,25 +110881,25 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
109997
110881
  need4(Array.isArray(groups), "DECLARATION_CHANGED");
109998
110882
  const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
109999
110883
  need4(matches2.length === 1 && isDeepStrictEqual13(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
110000
- expected.push({ event: nativeEvent, key: `${join58(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
110884
+ expected.push({ event: nativeEvent, key: `${join59(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
110001
110885
  }
110002
110886
  codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
110003
110887
  rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
110004
110888
  need4(SUPPORTED_CODEX_HOOK_VERSIONS.includes(rpc.version), "NATIVE_UNSUPPORTED_VERSION");
110005
110889
  const list2 = async () => {
110006
110890
  const response = await rpc.request("hooks/list", { cwds: [home] });
110007
- const nativeRoots = [...new Set([physicalRoot, join58(home, ".codex")])];
110008
- const coexistenceWarnings = nativeRoots.flatMap((hooksRoot) => nativeRoots.map((configRoot) => `loading hooks from both ${join58(hooksRoot, "hooks.json")} and ${join58(configRoot, "config.toml")}; prefer a single representation for this layer`));
110891
+ const nativeRoots = [...new Set([physicalRoot, join59(home, ".codex")])];
110892
+ const coexistenceWarnings = nativeRoots.flatMap((hooksRoot) => nativeRoots.map((configRoot) => `loading hooks from both ${join59(hooksRoot, "hooks.json")} and ${join59(configRoot, "config.toml")}; prefer a single representation for this layer`));
110009
110893
  const entry = response?.data?.[0];
110010
110894
  need4(Array.isArray(response?.data) && response.data.length === 1 && entry.cwd === home && Array.isArray(entry.hooks) && entry.hooks.length <= 1e4 && Array.isArray(entry.errors) && entry.errors.length === 0 && Array.isArray(entry.warnings) && entry.warnings.length <= 1 && entry.warnings.every((warning) => typeof warning === "string" && coexistenceWarnings.includes(warning)), "NATIVE_DISCOVERY_REFUSED");
110011
110895
  return response.data[0].hooks;
110012
110896
  };
110013
110897
  const admit = (hooks) => expected.map((wanted) => {
110014
- const canonicalKey = wanted.key.replace(join58(home, ".codex/hooks.json"), hooksPath);
110898
+ const canonicalKey = wanted.key.replace(join59(home, ".codex/hooks.json"), hooksPath);
110015
110899
  const matches2 = hooks.filter((h2) => h2.key === wanted.key || h2.key === canonicalKey);
110016
110900
  need4(matches2.length === 1, "AMBIGUOUS_IDENTITY");
110017
110901
  const h = matches2[0];
110018
- need4(h.eventName === wanted.event && h.handlerType === "command" && h.command === wanted.command && h.matcher === null && h.timeoutSec === 15 && h.async === false && h.statusMessage === null && h.additionalContextLimit === null && [hooksPath, join58(home, ".codex/hooks.json")].includes(h.sourcePath) && h.source === "user" && h.pluginId === null && h.isManaged === false && /^sha256:[a-f0-9]{64}$/.test(h.currentHash) && typeof h.enabled === "boolean", "NATIVE_IDENTITY_CHANGED");
110902
+ need4(h.eventName === wanted.event && h.handlerType === "command" && h.command === wanted.command && h.matcher === null && h.timeoutSec === 15 && h.async === false && h.statusMessage === null && h.additionalContextLimit === null && [hooksPath, join59(home, ".codex/hooks.json")].includes(h.sourcePath) && h.source === "user" && h.pluginId === null && h.isManaged === false && /^sha256:[a-f0-9]{64}$/.test(h.currentHash) && typeof h.enabled === "boolean", "NATIVE_IDENTITY_CHANGED");
110019
110903
  need4(hooks.filter((other) => other.command === wanted.command).length === 1, "AMBIGUOUS_IDENTITY");
110020
110904
  need4(["trusted", "untrusted", "modified"].includes(h.trustStatus), "UNKNOWN_TRUST_STATUS");
110021
110905
  return { key: h.key, event: wanted.event, command: wanted.command, handlerType: h.handlerType, timeoutSec: h.timeoutSec, sourcePath: h.sourcePath, currentHash: h.currentHash, enabled: h.enabled, trustStatus: h.trustStatus };
@@ -110041,27 +110925,27 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
110041
110925
  const baseReceipt = { planDigest, skillsCli: skillsCli.receipt, nativeVersion: rpc.version, nativeConfigPath, ownedNativeProcessId: rpc.processId, agent: "codex", applied: false, planned, nativeEligible: planned.length === 0, bindingRefreshRequired: false, transport: "owned-stdio-process", existingSessionsReloaded: false, nativeExecutionVerified: false };
110042
110926
  if (options.apply)
110043
110927
  need4(options.reviewedPlanDigest === planDigest, "PLAN_CHANGED: run the dry-run again and pass its reviewed --plan-digest with --apply");
110044
- const parent = join58(dataDir3, "native-hook-trust");
110045
- const st = lstatSync29(parent, { throwIfNoEntry: false });
110928
+ const parent = join59(dataDir3, "native-hook-trust");
110929
+ const st = lstatSync30(parent, { throwIfNoEntry: false });
110046
110930
  if (st) {
110047
110931
  need4(st.isDirectory() && !st.isSymbolicLink() && st.uid === process.getuid() && (st.mode & 511) === 448, "UNSAFE_JOURNAL");
110048
- const priorJournals = readdirSync21(parent);
110932
+ const priorJournals = readdirSync22(parent);
110049
110933
  need4(priorJournals.length < 1000, "JOURNAL_BOUND");
110050
110934
  for (const name of priorJournals) {
110051
110935
  need4(/^[a-f0-9-]{36}$/.test(name), "UNSAFE_JOURNAL");
110052
- need4(lstatSync29(join58(parent, name, "receipt.json"), { throwIfNoEntry: false })?.isFile(), "RECONCILE_REQUIRED: inspect the incomplete private journal before applying again");
110936
+ need4(lstatSync30(join59(parent, name, "receipt.json"), { throwIfNoEntry: false })?.isFile(), "RECONCILE_REQUIRED: inspect the incomplete private journal before applying again");
110053
110937
  }
110054
110938
  }
110055
110939
  if (!options.apply || !planned.length)
110056
110940
  return baseReceipt;
110057
110941
  if (!st)
110058
- mkdirSync26(parent, { mode: 448 });
110059
- journal = join58(parent, randomUUID10());
110060
- mkdirSync26(journal, { mode: 448 });
110061
- save(join58(journal, "config.before.toml"), configFile.bytes);
110062
- save(join58(journal, "hooks.before.json"), hooksFile.bytes);
110063
- save(join58(journal, "policy.before.json"), policyFile.bytes);
110064
- save(join58(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
110942
+ mkdirSync27(parent, { mode: 448 });
110943
+ journal = join59(parent, randomUUID11());
110944
+ mkdirSync27(journal, { mode: 448 });
110945
+ save(join59(journal, "config.before.toml"), configFile.bytes);
110946
+ save(join59(journal, "hooks.before.json"), hooksFile.bytes);
110947
+ save(join59(journal, "policy.before.json"), policyFile.bytes);
110948
+ save(join59(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
110065
110949
  need4(isDeepStrictEqual13(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
110066
110950
  checkInputs();
110067
110951
  let result2;
@@ -110077,7 +110961,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
110077
110961
  for (const h of planned)
110078
110962
  expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
110079
110963
  need4(isDeepStrictEqual13(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
110080
- const savedConfig = snapshot(join58(journal, "config.before.toml"), true);
110964
+ const savedConfig = snapshot(join59(journal, "config.before.toml"), true);
110081
110965
  need4(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
110082
110966
  const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
110083
110967
  unchanged2(policyFile);
@@ -110093,13 +110977,13 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
110093
110977
  skillsCli.recheck();
110094
110978
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
110095
110979
  const receipt = { ...baseReceipt, applied: true, nativeEligible: true, bindingRefreshRequired: !!discoveryTransition, ...discoveryTransition ? { discoveryTransition } : {}, nativeConfigVersion: result2.version, journal, beforeSha256: configFile.sha256, afterSha256: after2.sha256, unrelatedSettingsAndCommentsPreserved: true };
110096
- save(join58(journal, "receipt.json"), json2(receipt));
110980
+ save(join59(journal, "receipt.json"), json2(receipt));
110097
110981
  return receipt;
110098
110982
  } catch (error61) {
110099
110983
  const message = error61 instanceof Error && error61.message.startsWith("CODEX_HOOK_TRUST_") ? error61.message : "CODEX_HOOK_TRUST_REFUSED: verify managed bridge installation and native Codex support";
110100
110984
  if (journal) {
110101
110985
  try {
110102
- save(join58(journal, "stopped.json"), json2({ error: message.split(":")[0], automaticRollback: false, reconcileBeforeRetry: true }));
110986
+ save(join59(journal, "stopped.json"), json2({ error: message.split(":")[0], automaticRollback: false, reconcileBeforeRetry: true }));
110103
110987
  } catch {}
110104
110988
  }
110105
110989
  throw new Error(message);
@@ -110108,7 +110992,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
110108
110992
  }
110109
110993
  }
110110
110994
  var json2 = (value) => JSON.stringify(value, null, 2) + `
110111
- `, structure3 = (text4) => JSON.parse(JSON.stringify(Bun.TOML.parse(text4))), eventNames, makePlanDigest = (value) => createHash28("sha256").update(json2(value)).digest("hex"), safeNativeHook = (hook) => ({ key: hook.key, eventName: hook.eventName, handlerType: hook.handlerType, command: hook.command, matcher: hook.matcher, timeoutSec: hook.timeoutSec, async: hook.async, statusMessage: hook.statusMessage, additionalContextLimit: hook.additionalContextLimit, sourcePath: hook.sourcePath, source: hook.source, pluginId: hook.pluginId, isManaged: hook.isManaged, currentHash: hook.currentHash, enabled: hook.enabled, trustStatus: hook.trustStatus }), withoutCodexCommand = (value) => {
110995
+ `, structure3 = (text4) => JSON.parse(JSON.stringify(Bun.TOML.parse(text4))), eventNames, makePlanDigest = (value) => createHash29("sha256").update(json2(value)).digest("hex"), safeNativeHook = (hook) => ({ key: hook.key, eventName: hook.eventName, handlerType: hook.handlerType, command: hook.command, matcher: hook.matcher, timeoutSec: hook.timeoutSec, async: hook.async, statusMessage: hook.statusMessage, additionalContextLimit: hook.additionalContextLimit, sourcePath: hook.sourcePath, source: hook.source, pluginId: hook.pluginId, isManaged: hook.isManaged, currentHash: hook.currentHash, enabled: hook.enabled, trustStatus: hook.trustStatus }), withoutCodexCommand = (value) => {
110112
110996
  const copy = structuredClone(value);
110113
110997
  if (copy?.bridge?.commands && Object.hasOwn(copy.bridge.commands, "codex"))
110114
110998
  copy.bridge.commands.codex = "<reviewed-codex-binding>";
@@ -110219,8 +111103,8 @@ var exports_agent_integration = {};
110219
111103
  __export(exports_agent_integration, {
110220
111104
  registerAgentIntegration: () => registerAgentIntegration
110221
111105
  });
110222
- import { readFileSync as readFileSync40 } from "fs";
110223
- import { isAbsolute as isAbsolute27 } from "path";
111106
+ import { readFileSync as readFileSync41 } from "fs";
111107
+ import { isAbsolute as isAbsolute28 } from "path";
110224
111108
  function pinnedHookProfile(input3, configuredProfile) {
110225
111109
  try {
110226
111110
  const parsed = parseSkillContextInput(JSON.stringify(input3));
@@ -110297,7 +111181,7 @@ function registerAgentIntegration(parent) {
110297
111181
  });
110298
111182
  hook.command("install").option("--agent <agent>", `Agent to configure: ${INTEGRATION_AGENTS.join(", ")}, all`, "all").option("--command <path>", "Skills executable used by the hook (preserves existing binding; new agents use skills)").option("--selection-profile <id>", "Selection profile (preserves existing binding; new agents use default)").option("--include-vendor", "Retained for compatibility; vendor system skills are always inventoried and disabled", false).option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes for unsupported registrations").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Apply the plan, preserving prior configuration in private backups", false).option("--json", "Output a receipt as JSON", false).description("Plan or install one Skills CLI bridge plus native prompt hooks").action(async (options) => {
110299
111183
  try {
110300
- const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync40(options.discoveryInputs, "utf8")) : undefined;
111184
+ const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync41(options.discoveryInputs, "utf8")) : undefined;
110301
111185
  const plan = planAgentIntegration({ agents: agents(options.agent), command: options.command, profileId: options.selectionProfile, includeVendor: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases });
110302
111186
  const result2 = options.apply ? applyAgentIntegration(plan) : { changed: [], backups: [] };
110303
111187
  const receipt = { applied: options.apply, planned: plan.changes.map((change) => change.path), ...result2, rootAliases: plan.rootAliases ?? [], discovery: plan.discoveryAfter, nativeSkills: plan.nativeSkills.map((entry) => ({ agent: entry.agent, path: entry.path, managed: entry.managed, vendor: entry.vendor, system: entry.system === true, bridge: entry.bridge === true })), requiresNativeRetirement: plan.nativeSkills.some((entry) => !entry.bridge && !entry.system) };
@@ -110355,7 +111239,7 @@ function registerAgentIntegration(parent) {
110355
111239
  try {
110356
111240
  if (agents(options.agent).length !== 1)
110357
111241
  throw new Error("A hook invocation requires one agent");
110358
- const inputText = readFileSync40(0, "utf8");
111242
+ const inputText = readFileSync41(0, "utf8");
110359
111243
  if (inputText.length > 1024 * 1024)
110360
111244
  throw new Error("Hook input is too large");
110361
111245
  let input3 = JSON.parse(inputText);
@@ -110368,12 +111252,12 @@ function registerAgentIntegration(parent) {
110368
111252
  input3.hook_event_name = event;
110369
111253
  const projects = [process.cwd()];
110370
111254
  if (input3.cwd !== undefined) {
110371
- if (typeof input3.cwd !== "string" || !isAbsolute27(input3.cwd) || input3.cwd.includes("\x00"))
111255
+ if (typeof input3.cwd !== "string" || !isAbsolute28(input3.cwd) || input3.cwd.includes("\x00"))
110372
111256
  throw new Error("Invalid native hook working directory");
110373
111257
  projects.push(input3.cwd);
110374
111258
  }
110375
111259
  if (options.agent === "cursor") {
110376
- if (!Array.isArray(input3.workspace_roots) || input3.workspace_roots.length > 32 || input3.workspace_roots.some((path) => typeof path !== "string" || !isAbsolute27(path) || path.includes("\x00")))
111260
+ if (!Array.isArray(input3.workspace_roots) || input3.workspace_roots.length > 32 || input3.workspace_roots.some((path) => typeof path !== "string" || !isAbsolute28(path) || path.includes("\x00")))
110377
111261
  throw new Error("Invalid Cursor workspace roots");
110378
111262
  projects.push(...input3.workspace_roots);
110379
111263
  input3.cwd ??= input3.workspace_roots[0] ?? process.cwd();
@@ -110453,7 +111337,7 @@ function registerAgentIntegration(parent) {
110453
111337
  try {
110454
111338
  if (options.agent && !INTEGRATION_AGENTS.includes(options.agent))
110455
111339
  throw new Error(`Unsupported migration agent: ${options.agent}`);
110456
- const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync40(options.discoveryInputs, "utf8")) : undefined;
111340
+ const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync41(options.discoveryInputs, "utf8")) : undefined;
110457
111341
  if (options.targetManifest && (options.includeUnmanaged || options.includeVendor))
110458
111342
  throw new Error("--target-manifest cannot be combined with broad native migration selectors");
110459
111343
  const targetManifest = options.targetManifest ? readNativeMigrationTargetManifest(options.targetManifest) : undefined;
@@ -110623,7 +111507,7 @@ var exports_profiles = {};
110623
111507
  __export(exports_profiles, {
110624
111508
  registerProfiles: () => registerProfiles
110625
111509
  });
110626
- import { closeSync as closeSync23, constants as constants20, fstatSync as fstatSync23, openSync as openSync23, readSync as readSync17, writeFileSync as writeFileSync25 } from "fs";
111510
+ import { closeSync as closeSync23, constants as constants20, fstatSync as fstatSync23, openSync as openSync23, readSync as readSync17, writeFileSync as writeFileSync26 } from "fs";
110627
111511
  function readProfileInput(path) {
110628
111512
  const fd = openSync23(path, constants20.O_RDONLY | constants20.O_NONBLOCK);
110629
111513
  try {
@@ -110651,7 +111535,7 @@ function registerProfiles(parent) {
110651
111535
  try {
110652
111536
  const profile = await readSkillProfile(id3), text4 = JSON.stringify(profile, null, 2);
110653
111537
  if (options.save)
110654
- writeFileSync25(options.save, JSON.stringify(profile), { mode: 384, flag: "wx" });
111538
+ writeFileSync26(options.save, JSON.stringify(profile), { mode: 384, flag: "wx" });
110655
111539
  await writeCliOutput(options.json ? text4 : `${profile.id} at ${profile.revision}: ${profile.selections.map((skill) => `${skill.slug}@${skill.version}`).join(", ")}`);
110656
111540
  } catch (error61) {
110657
111541
  console.error(error61.message);
@@ -110698,7 +111582,7 @@ import {
110698
111582
  fstatSync as fstatSync24,
110699
111583
  openSync as openSync24,
110700
111584
  readSync as readSync18,
110701
- writeFileSync as writeFileSync26
111585
+ writeFileSync as writeFileSync27
110702
111586
  } from "fs";
110703
111587
  function readInput(path) {
110704
111588
  const fd = openSync24(path, constants21.O_RDONLY | constants21.O_NONBLOCK);
@@ -110731,7 +111615,7 @@ function registerGrants(parent) {
110731
111615
  try {
110732
111616
  const policy = await readExecutionGrantPolicy(profile, options.revision);
110733
111617
  if (options.save)
110734
- writeFileSync26(options.save, JSON.stringify(policy, null, 2), {
111618
+ writeFileSync27(options.save, JSON.stringify(policy, null, 2), {
110735
111619
  mode: 384,
110736
111620
  flag: "wx"
110737
111621
  });
@@ -110758,28 +111642,28 @@ var init_grants = __esm(() => {
110758
111642
  });
110759
111643
 
110760
111644
  // src/lib/station-hydrate.ts
110761
- import { createHash as createHash29 } from "crypto";
111645
+ import { createHash as createHash30 } from "crypto";
110762
111646
  import {
110763
- copyFileSync as copyFileSync2,
110764
- mkdirSync as mkdirSync27,
110765
- readdirSync as readdirSync22,
110766
- readFileSync as readFileSync41,
111647
+ copyFileSync as copyFileSync3,
111648
+ mkdirSync as mkdirSync28,
111649
+ readdirSync as readdirSync23,
111650
+ readFileSync as readFileSync42,
110767
111651
  statSync as statSync22,
110768
- writeFileSync as writeFileSync27
111652
+ writeFileSync as writeFileSync28
110769
111653
  } from "fs";
110770
- import { dirname as dirname33, join as join59, resolve as resolve24, sep as sep11 } from "path";
111654
+ import { dirname as dirname34, join as join60, resolve as resolve25, sep as sep12 } from "path";
110771
111655
  function fail5(code2, message, detail = []) {
110772
111656
  throw new StationSnapshotError(code2, message, detail);
110773
111657
  }
110774
111658
  function snapshotRootFor(repoRoot, stationId) {
110775
- return join59(repoRoot, "resources", stationId, "skills");
111659
+ return join60(repoRoot, "resources", stationId, "skills");
110776
111660
  }
110777
111661
  function readSnapshotManifest(repoRoot, stationId) {
110778
111662
  const snapshotRoot = snapshotRootFor(repoRoot, stationId);
110779
- const manifestPath = join59(snapshotRoot, "sync-manifest.json");
111663
+ const manifestPath = join60(snapshotRoot, "sync-manifest.json");
110780
111664
  let manifest;
110781
111665
  try {
110782
- manifest = JSON.parse(readFileSync41(manifestPath, "utf8"));
111666
+ manifest = JSON.parse(readFileSync42(manifestPath, "utf8"));
110783
111667
  } catch (error61) {
110784
111668
  fail5("MANIFEST_UNREADABLE", `cannot read snapshot manifest: ${manifestPath}: ${error61.message}`);
110785
111669
  }
@@ -110803,10 +111687,10 @@ function planStationHydration(stationId, repoRoot) {
110803
111687
  const hashMismatches = [];
110804
111688
  const skippedByRule = [];
110805
111689
  for (const agent of SYNC_AGENTS) {
110806
- const agentRoot = join59(snapshotRoot, "agent-homes", agent);
111690
+ const agentRoot = join60(snapshotRoot, "agent-homes", agent);
110807
111691
  let identEntries;
110808
111692
  try {
110809
- identEntries = readdirSync22(agentRoot, { withFileTypes: true });
111693
+ identEntries = readdirSync23(agentRoot, { withFileTypes: true });
110810
111694
  } catch {
110811
111695
  continue;
110812
111696
  }
@@ -110814,10 +111698,10 @@ function planStationHydration(stationId, repoRoot) {
110814
111698
  if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
110815
111699
  continue;
110816
111700
  }
110817
- const identRoot = join59(agentRoot, identEntry.name);
111701
+ const identRoot = join60(agentRoot, identEntry.name);
110818
111702
  const entries = walkEntries(identRoot);
110819
111703
  for (const entry of entries) {
110820
- const relativeParts = [identEntry.name, ...entry.relativePath.split(sep11)];
111704
+ const relativeParts = [identEntry.name, ...entry.relativePath.split(sep12)];
110821
111705
  if (entry.kind === "symlink") {
110822
111706
  symlinks.push({ ident: identEntry.name, agent, relativePath: entry.relativePath });
110823
111707
  continue;
@@ -110841,8 +111725,8 @@ function planStationHydration(stationId, repoRoot) {
110841
111725
  });
110842
111726
  continue;
110843
111727
  }
110844
- const withinIdent = relativeParts.slice(1).join(sep11);
110845
- const homeRelative = relativeParts.join(sep11);
111728
+ const withinIdent = relativeParts.slice(1).join(sep12);
111729
+ const homeRelative = relativeParts.join(sep12);
110846
111730
  if (REFUSED_SCANNER_FLAGGED.has(homeRelative)) {
110847
111731
  skippedByRule.push({
110848
111732
  ident: identEntry.name,
@@ -110915,7 +111799,7 @@ function planStationHydration(stationId, repoRoot) {
110915
111799
  for (const copy of copies) {
110916
111800
  let isStub = false;
110917
111801
  try {
110918
- isStub = isPointerSkillMd(readFileSync41(copy.fullPath, "utf8"));
111802
+ isStub = isPointerSkillMd(readFileSync42(copy.fullPath, "utf8"));
110919
111803
  } catch {
110920
111804
  isStub = false;
110921
111805
  }
@@ -110961,12 +111845,12 @@ function skillSha256(skill) {
110961
111845
  return sha256File(skill.files[0].winner.fullPath);
110962
111846
  }
110963
111847
  const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
110964
- return createHash29("sha256").update(joined.sort().join(`
111848
+ return createHash30("sha256").update(joined.sort().join(`
110965
111849
  `)).digest("hex");
110966
111850
  }
110967
111851
  function writeStationHydration(options) {
110968
- const repoRoot = resolve24(options.repoRoot ?? process.cwd());
110969
- const cacheRoot = resolve24(options.cacheRoot ?? resolveCorpusRoot());
111852
+ const repoRoot = resolve25(options.repoRoot ?? process.cwd());
111853
+ const cacheRoot = resolve25(options.cacheRoot ?? resolveCorpusRoot());
110970
111854
  const plan = planStationHydration(options.stationId, repoRoot);
110971
111855
  const resultSkills = plan.winners.map((skill) => ({
110972
111856
  ident: skill.ident,
@@ -110999,7 +111883,7 @@ function writeStationHydration(options) {
110999
111883
  const toWrite = [];
111000
111884
  for (const skill of plan.winners) {
111001
111885
  for (const file2 of skill.files) {
111002
- const destination = join59(cacheRoot, skill.ident, file2.withinIdent);
111886
+ const destination = join60(cacheRoot, skill.ident, file2.withinIdent);
111003
111887
  const digest4 = sha256File(file2.winner.fullPath);
111004
111888
  let existingDigest = null;
111005
111889
  try {
@@ -111020,8 +111904,8 @@ function writeStationHydration(options) {
111020
111904
  }
111021
111905
  let written = 0;
111022
111906
  for (const entry of toWrite) {
111023
- mkdirSync27(dirname33(entry.destination), { recursive: true });
111024
- copyFileSync2(entry.fullPath, entry.destination);
111907
+ mkdirSync28(dirname34(entry.destination), { recursive: true });
111908
+ copyFileSync3(entry.fullPath, entry.destination);
111025
111909
  written += 1;
111026
111910
  }
111027
111911
  const unchanged3 = plan.totalFiles - written;
@@ -111041,9 +111925,9 @@ function writeStationHydration(options) {
111041
111925
  },
111042
111926
  skills: resultSkills
111043
111927
  };
111044
- const hydrationManifestPath = join59(dirname33(cacheRoot), `hydration-${options.stationId}.json`);
111045
- mkdirSync27(dirname33(hydrationManifestPath), { recursive: true });
111046
- writeFileSync27(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
111928
+ const hydrationManifestPath = join60(dirname34(cacheRoot), `hydration-${options.stationId}.json`);
111929
+ mkdirSync28(dirname34(hydrationManifestPath), { recursive: true });
111930
+ writeFileSync28(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
111047
111931
  `);
111048
111932
  return {
111049
111933
  ...base3,
@@ -111118,15 +112002,15 @@ var init_hydrate = __esm(() => {
111118
112002
  });
111119
112003
 
111120
112004
  // src/lib/prepare-skill.ts
111121
- import { createHash as createHash30, randomUUID as randomUUID11 } from "crypto";
111122
- import { chmodSync as chmodSync4, existsSync as existsSync34, lstatSync as lstatSync30, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync6, readFileSync as readFileSync42, readdirSync as readdirSync23, renameSync as renameSync13, rmSync as rmSync9, writeFileSync as writeFileSync28 } from "fs";
112005
+ import { createHash as createHash31, randomUUID as randomUUID12 } from "crypto";
112006
+ import { chmodSync as chmodSync5, existsSync as existsSync35, lstatSync as lstatSync31, mkdirSync as mkdirSync29, mkdtempSync as mkdtempSync6, readFileSync as readFileSync43, readdirSync as readdirSync24, renameSync as renameSync14, rmSync as rmSync9, writeFileSync as writeFileSync29 } from "fs";
111123
112007
  import { tmpdir as tmpdir2 } from "os";
111124
- import { dirname as dirname34, isAbsolute as isAbsolute28, join as join60, relative as relative9, resolve as resolve25, sep as sep12 } from "path";
112008
+ import { dirname as dirname35, isAbsolute as isAbsolute29, join as join61, relative as relative10, resolve as resolve26, sep as sep13 } from "path";
111125
112009
  function prepareSkill(name, options) {
111126
112010
  const normalized = normalizePortableSkillName(name);
111127
112011
  if (!new RegExp(SEMVER_PATTERN).test(options.version))
111128
112012
  throw new Error("--version must be a semantic version, for example 0.2.0.");
111129
- const path = join60(getPortableSkillsRootReadOnly(options), normalized);
112013
+ const path = join61(getPortableSkillsRootReadOnly(options), normalized);
111130
112014
  assertPortableAuthoringPath(path, options);
111131
112015
  const before2 = snapshot2(path);
111132
112016
  const manifestFile = before2.files.find((file2) => file2.path === "skill.json");
@@ -111161,17 +112045,17 @@ function prepareSkill(name, options) {
111161
112045
  throw new Error("skill.json provenance must be an object.");
111162
112046
  const provenance = manifest.provenance;
111163
112047
  const candidate2 = { ...manifest, version: options.version, kind, provenance: { ...provenance } };
111164
- const staging = mkdtempSync6(join60(tmpdir2(), "skills-prepare-"));
112048
+ const staging = mkdtempSync6(join61(tmpdir2(), "skills-prepare-"));
111165
112049
  try {
111166
112050
  for (const directory2 of before2.directories)
111167
- mkdirSync28(join60(staging, directory2), { recursive: true });
112051
+ mkdirSync29(join61(staging, directory2), { recursive: true });
111168
112052
  for (const file2 of before2.files) {
111169
- const destination = join60(staging, file2.path);
111170
- mkdirSync28(dirname34(destination), { recursive: true });
111171
- writeFileSync28(destination, file2.bytes, { mode: file2.mode });
112053
+ const destination = join61(staging, file2.path);
112054
+ mkdirSync29(dirname35(destination), { recursive: true });
112055
+ writeFileSync29(destination, file2.bytes, { mode: file2.mode });
111172
112056
  }
111173
- const stagedManifest = join60(staging, "skill.json");
111174
- writeFileSync28(stagedManifest, `${JSON.stringify(candidate2, null, 2)}
112057
+ const stagedManifest = join61(staging, "skill.json");
112058
+ writeFileSync29(stagedManifest, `${JSON.stringify(candidate2, null, 2)}
111175
112059
  `);
111176
112060
  const contentHash = computeContentHash(staging);
111177
112061
  const changed3 = options.version !== previousVersion || kind !== manifest.kind || contentHash !== provenance?.content_hash;
@@ -111181,7 +112065,7 @@ function prepareSkill(name, options) {
111181
112065
  candidate2.provenance.content_hash = contentHash;
111182
112066
  const candidateBytes = `${JSON.stringify(candidate2, null, 2)}
111183
112067
  `;
111184
- writeFileSync28(stagedManifest, candidateBytes);
112068
+ writeFileSync29(stagedManifest, candidateBytes);
111185
112069
  validateRawCandidate(candidate2);
111186
112070
  validateRuntimeEntrypoint(candidate2, staging);
111187
112071
  const validation = validatePortableSkillDirectory(normalized, staging);
@@ -111193,11 +112077,11 @@ ${validation.issues.map((issue2) => `${issue2.code}: ${issue2.message}`).join(`
111193
112077
  if (snapshot2(path).identity !== before2.identity)
111194
112078
  throw new Error("Skill changed during preparation; review the draft and run prepare again.");
111195
112079
  if (changed3 && !options.dryRun) {
111196
- const temporary = join60(path, `.skill-prepare-${randomUUID11()}.tmp`);
112080
+ const temporary = join61(path, `.skill-prepare-${randomUUID12()}.tmp`);
111197
112081
  try {
111198
- writeFileSync28(temporary, candidateBytes, { flag: "wx", mode: manifestFile.mode });
111199
- chmodSync4(temporary, manifestFile.mode);
111200
- renameSync13(temporary, join60(path, "skill.json"));
112082
+ writeFileSync29(temporary, candidateBytes, { flag: "wx", mode: manifestFile.mode });
112083
+ chmodSync5(temporary, manifestFile.mode);
112084
+ renameSync14(temporary, join61(path, "skill.json"));
111201
112085
  } finally {
111202
112086
  rmSync9(temporary, { force: true });
111203
112087
  }
@@ -111227,8 +112111,8 @@ function validateRuntimeEntrypoint(candidate2, root2) {
111227
112111
  const entry = candidate2.runtime.entrypoint;
111228
112112
  if (typeof entry !== "string")
111229
112113
  return;
111230
- const target = resolve25(root2, entry), rel = relative9(root2, target);
111231
- if (isAbsolute28(entry) || /^[a-zA-Z]:/.test(entry) || entry.includes("\\") || !rel || rel === ".." || rel.startsWith(`..${sep12}`) || isAbsolute28(rel) || !existsSync34(target) || !lstatSync30(target).isFile()) {
112114
+ const target = resolve26(root2, entry), rel = relative10(root2, target);
112115
+ if (isAbsolute29(entry) || /^[a-zA-Z]:/.test(entry) || entry.includes("\\") || !rel || rel === ".." || rel.startsWith(`..${sep13}`) || isAbsolute29(rel) || !existsSync35(target) || !lstatSync31(target).isFile()) {
111232
112116
  throw new Error("Executable runtime.entrypoint must name an existing regular file inside the skill directory.");
111233
112117
  }
111234
112118
  }
@@ -111313,25 +112197,25 @@ function compareVersions(left, right) {
111313
112197
  return 0;
111314
112198
  }
111315
112199
  function snapshot2(root2) {
111316
- const rootStat = lstatSync30(root2);
112200
+ const rootStat = lstatSync31(root2);
111317
112201
  if (rootStat.isSymbolicLink() || !rootStat.isDirectory())
111318
112202
  throw new Error("Skill root must be a real directory, not a symlink.");
111319
112203
  const files = [], directories = [];
111320
- const hash5 = createHash30("sha256").update(`${rootStat.dev}:${rootStat.ino}\x00`);
112204
+ const hash6 = createHash31("sha256").update(`${rootStat.dev}:${rootStat.ino}\x00`);
111321
112205
  let count = 0, bytes = 0;
111322
- const walk2 = (relative10) => {
111323
- for (const name of readdirSync23(join60(root2, relative10)).sort()) {
111324
- if (IGNORED.has(name.toLowerCase()) || !relative10 && ROOT_IGNORED.has(name.toLowerCase()))
112206
+ const walk2 = (relative11) => {
112207
+ for (const name of readdirSync24(join61(root2, relative11)).sort()) {
112208
+ if (IGNORED.has(name.toLowerCase()) || !relative11 && ROOT_IGNORED.has(name.toLowerCase()))
111325
112209
  continue;
111326
- const path = relative10 ? `${relative10}/${name}` : name;
112210
+ const path = relative11 ? `${relative11}/${name}` : name;
111327
112211
  if (++count > 20000)
111328
112212
  throw new Error("Skill exceeds the preparation entry limit (20000).");
111329
- const absolute = join60(root2, path), stat3 = lstatSync30(absolute);
112213
+ const absolute = join61(root2, path), stat3 = lstatSync31(absolute);
111330
112214
  if (stat3.isSymbolicLink())
111331
112215
  throw new Error(`Skill contains a symlink: ${path}. No files were changed.`);
111332
112216
  if (stat3.isDirectory() && name === "node_modules")
111333
112217
  continue;
111334
- hash5.update(JSON.stringify([path, stat3.dev, stat3.ino, stat3.mode]));
112218
+ hash6.update(JSON.stringify([path, stat3.dev, stat3.ino, stat3.mode]));
111335
112219
  if (stat3.isDirectory()) {
111336
112220
  directories.push(path);
111337
112221
  walk2(path);
@@ -111339,18 +112223,18 @@ function snapshot2(root2) {
111339
112223
  bytes += stat3.size;
111340
112224
  if (bytes > 50000000)
111341
112225
  throw new Error("Skill exceeds the preparation size limit (50000000 bytes).");
111342
- const content = readFileSync42(absolute);
112226
+ const content = readFileSync43(absolute);
111343
112227
  if (content.length !== stat3.size)
111344
112228
  throw new Error("Skill changed during preparation.");
111345
112229
  files.push({ path, bytes: content, mode: stat3.mode & 511 });
111346
- hash5.update(`\x00${content.length}\x00`).update(content);
112230
+ hash6.update(`\x00${content.length}\x00`).update(content);
111347
112231
  } else
111348
112232
  throw new Error(`Unsupported skill entry: ${path}.`);
111349
- hash5.update("\x00");
112233
+ hash6.update("\x00");
111350
112234
  }
111351
112235
  };
111352
112236
  walk2("");
111353
- return { files, directories, identity: hash5.digest("hex") };
112237
+ return { files, directories, identity: hash6.digest("hex") };
111354
112238
  }
111355
112239
  var MAX_PACK_PARITY_PATHS = 8, MAX_PACK_PARITY_PATH_CHARS = 120, IGNORED, ROOT_IGNORED;
111356
112240
  var init_prepare_skill = __esm(() => {
@@ -111741,8 +112625,8 @@ var init_schedule = __esm(() => {
111741
112625
  });
111742
112626
 
111743
112627
  // src/lib/registry-sync.ts
111744
- import { mkdirSync as mkdirSync29, writeFileSync as writeFileSync29 } from "fs";
111745
- import { dirname as dirname35, relative as relative10 } from "path";
112628
+ import { mkdirSync as mkdirSync30, writeFileSync as writeFileSync30 } from "fs";
112629
+ import { dirname as dirname36, relative as relative11 } from "path";
111746
112630
  function createRegistrySyncArtifact(options = {}) {
111747
112631
  const profile = options.profile ?? "all";
111748
112632
  const includeDocs = options.includeDocs ?? true;
@@ -111754,7 +112638,7 @@ function createRegistrySyncArtifact(options = {}) {
111754
112638
  const registry2 = [...loadRegistryProfile(profile)].sort((a, b) => a.name.localeCompare(b.name));
111755
112639
  const skills = registry2.map((skill) => {
111756
112640
  const skillPath = getSkillPath(skill.name);
111757
- const directory2 = relative10(process.cwd(), skillPath) || skillPath;
112641
+ const directory2 = relative11(process.cwd(), skillPath) || skillPath;
111758
112642
  const validation = includeValidation ? validateSkillDirectory(skill.name, skillPath, skill) : undefined;
111759
112643
  const docs = includeDocs ? buildDocs(skill.name) : undefined;
111760
112644
  return {
@@ -111799,8 +112683,8 @@ function createRegistrySyncArtifact(options = {}) {
111799
112683
  };
111800
112684
  }
111801
112685
  function writeRegistrySyncArtifact(path, artifact) {
111802
- mkdirSync29(dirname35(path), { recursive: true });
111803
- writeFileSync29(path, `${JSON.stringify(artifact, null, 2)}
112686
+ mkdirSync30(dirname36(path), { recursive: true });
112687
+ writeFileSync30(path, `${JSON.stringify(artifact, null, 2)}
111804
112688
  `);
111805
112689
  }
111806
112690
  function buildDocs(name) {
@@ -111819,8 +112703,8 @@ var init_registry_sync = __esm(() => {
111819
112703
  });
111820
112704
 
111821
112705
  // src/lib/review-export.ts
111822
- import { constants as constants22, closeSync as closeSync25, existsSync as existsSync35, fstatSync as fstatSync25, fsyncSync as fsyncSync7, lstatSync as lstatSync31, openSync as openSync25, realpathSync as realpathSync15, statSync as statSync23, writeFileSync as writeFileSync30 } from "fs";
111823
- import { basename as basename14, dirname as dirname36, isAbsolute as isAbsolute29, join as join61, parse as parse12, resolve as resolve26, sep as sep13 } from "path";
112706
+ import { constants as constants22, closeSync as closeSync25, existsSync as existsSync36, fstatSync as fstatSync25, fsyncSync as fsyncSync7, lstatSync as lstatSync32, openSync as openSync25, realpathSync as realpathSync16, statSync as statSync23, writeFileSync as writeFileSync31 } from "fs";
112707
+ import { basename as basename15, dirname as dirname37, isAbsolute as isAbsolute30, join as join62, parse as parse12, resolve as resolve27, sep as sep14 } from "path";
111824
112708
  function exactVersion(spec) {
111825
112709
  const at2 = spec.indexOf("@");
111826
112710
  const slug2 = spec.slice(0, at2);
@@ -111831,23 +112715,23 @@ function exactVersion(spec) {
111831
112715
  return { slug: slug2, version: version2 };
111832
112716
  }
111833
112717
  function assertPrivateDestination(output2) {
111834
- if (!isAbsolute29(output2) || resolve26(output2) !== output2 || !basename14(output2).endsWith(".tar.gz")) {
112718
+ if (!isAbsolute30(output2) || resolve27(output2) !== output2 || !basename15(output2).endsWith(".tar.gz")) {
111835
112719
  throw new Error("Review output must be an absolute, normalized .tar.gz path.");
111836
112720
  }
111837
- const parent = dirname36(output2);
111838
- if (realpathSync15(parent) !== parent || lstatSync31(parent).isSymbolicLink()) {
112721
+ const parent = dirname37(output2);
112722
+ if (realpathSync16(parent) !== parent || lstatSync32(parent).isSymbolicLink()) {
111839
112723
  throw new Error("Review output parent must be a real directory, not a symlink.");
111840
112724
  }
111841
112725
  const parentStat = statSync23(parent);
111842
112726
  if (!parentStat.isDirectory() || (parentStat.mode & 63) !== 0 || process.getuid && parentStat.uid !== process.getuid()) {
111843
112727
  throw new Error("Review output parent must be an owner-only directory (mode 0700).");
111844
112728
  }
111845
- const segments = parent.toLowerCase().split(sep13);
112729
+ const segments = parent.toLowerCase().split(sep14);
111846
112730
  if (segments.some((segment) => NATIVE_CONTAINERS.has(segment)) || segments.some((segment, index) => segment === ".config" && segments[index + 1] === "opencode") || segments.some((segment, index) => segment === ".hasna" && segments[index + 1] === "skills")) {
111847
112731
  throw new Error("Review output cannot be placed in agent-native discovery or Skills installation paths.");
111848
112732
  }
111849
- for (let current = parent;; current = dirname36(current)) {
111850
- if (existsSync35(join61(current, ".git"))) {
112733
+ for (let current = parent;; current = dirname37(current)) {
112734
+ if (existsSync36(join62(current, ".git"))) {
111851
112735
  throw new Error("Review output cannot be placed inside a Git worktree.");
111852
112736
  }
111853
112737
  if (current === parse12(current).root)
@@ -111858,7 +112742,7 @@ function assertPrivateDestination(output2) {
111858
112742
  async function exportSkillVersionForReview(spec, output2, client2) {
111859
112743
  const { slug: slug2, version: version2 } = exactVersion(spec);
111860
112744
  const target = assertPrivateDestination(output2);
111861
- if (existsSync35(target))
112745
+ if (existsSync36(target))
111862
112746
  throw new Error("Review output already exists; choose a new filename.");
111863
112747
  const metadata = await client2.getSkillVersion(slug2, version2);
111864
112748
  if (!metadata)
@@ -111885,7 +112769,7 @@ async function exportSkillVersionForReview(spec, output2, client2) {
111885
112769
  }
111886
112770
  const fd = openSync25(target, constants22.O_WRONLY | constants22.O_CREAT | constants22.O_EXCL | constants22.O_NOFOLLOW, 384);
111887
112771
  try {
111888
- writeFileSync30(fd, bytes);
112772
+ writeFileSync31(fd, bytes);
111889
112773
  fsyncSync7(fd);
111890
112774
  if (fstatSync25(fd).size !== bytes.byteLength)
111891
112775
  throw new Error("Incomplete review output write.");
@@ -111923,12 +112807,12 @@ function registerRegistry(parent) {
111923
112807
  async function writeJson2(value, space) {
111924
112808
  const text4 = `${JSON.stringify(value, null, space)}
111925
112809
  `;
111926
- await new Promise((resolve27, reject2) => {
112810
+ await new Promise((resolve28, reject2) => {
111927
112811
  process.stdout.write(text4, (error61) => {
111928
112812
  if (error61)
111929
112813
  reject2(error61);
111930
112814
  else
111931
- resolve27();
112815
+ resolve28();
111932
112816
  });
111933
112817
  });
111934
112818
  }
@@ -112132,9 +113016,9 @@ __export(exports_publish, {
112132
113016
  PushSkillError: () => PushSkillError
112133
113017
  });
112134
113018
  import { execFileSync as execFileSync2 } from "child_process";
112135
- import { existsSync as existsSync36, readFileSync as readFileSync43 } from "fs";
113019
+ import { existsSync as existsSync37, readFileSync as readFileSync44 } from "fs";
112136
113020
  import { hostname as hostname5 } from "os";
112137
- import { join as join62 } from "path";
113021
+ import { join as join63 } from "path";
112138
113022
  function registerPublish(parent) {
112139
113023
  parent.command("push").argument("<name>", "Name of a skill in the local corpus (~/.hasna/skills/installed or the migrated ~/.hasna/skills/skills)").option("--version <version>", "Override the version recorded on the instance").option("--force-new-version", "If name@version already exists with different content, publish as the next patch version", false).option("--dry-run", "Pack and validate without uploading", false).option("--json", "Output result as JSON", false).description("Publish a local skill to the configured skills instance").action(async (name, options) => {
112140
113024
  try {
@@ -112186,8 +113070,8 @@ async function pushSkill(name, options = {}) {
112186
113070
  const packed = packSkillBundle(skill.path, { maxUnpackedBytes: MAX_UNPACKED_BYTES });
112187
113071
  await verifyPackedCanonicalHash(skill.name, packed);
112188
113072
  const versionManifest = buildVersionManifest(skill.path, packed);
112189
- const skillMdPath = join62(skill.path, "SKILL.md");
112190
- const skillMd = existsSync36(skillMdPath) ? readFileSync43(skillMdPath, "utf-8") : undefined;
113073
+ const skillMdPath = join63(skill.path, "SKILL.md");
113074
+ const skillMd = existsSync37(skillMdPath) ? readFileSync44(skillMdPath, "utf-8") : undefined;
112191
113075
  const base3 = {
112192
113076
  slug: skill.name,
112193
113077
  path: skill.path,
@@ -112425,7 +113309,7 @@ var exports_private_publications = {};
112425
113309
  __export(exports_private_publications, {
112426
113310
  registerPrivatePublications: () => registerPrivatePublications
112427
113311
  });
112428
- import { resolve as resolve27 } from "path";
113312
+ import { resolve as resolve28 } from "path";
112429
113313
  function verifyOptions(options, action) {
112430
113314
  if (options.userId === undefined !== (options.membershipId === undefined) || options.userId !== undefined && (!publicationUuid(options.userId) || !publicationUuid(options.membershipId)))
112431
113315
  throw new PrivatePublicationError("PUBLICATION_CONTEXT_REQUIRED", "Provide both observed --user-id and --membership-id UUIDs.");
@@ -112470,9 +113354,9 @@ function registerPrivatePublications(parent) {
112470
113354
  if (code2 === null)
112471
113355
  return;
112472
113356
  const context2 = options.userId ? { userId: options.userId, membershipId: options.membershipId } : undefined;
112473
- const client2 = await privatePublicationSession(options.email, code2, context2), directory2 = resolve27(options.recoveryDir);
113357
+ const client2 = await privatePublicationSession(options.email, code2, context2), directory2 = resolve28(options.recoveryDir);
112474
113358
  if (action === "publish")
112475
- await preparePrivatePublication(client2, resolve27(args2[0]), directory2, { skillId: options.skillId, expectedCurrentVersionId: options.expectedCurrentVersion ?? null, idempotencyKey: options.idempotencyKey });
113359
+ await preparePrivatePublication(client2, resolve28(args2[0]), directory2, { skillId: options.skillId, expectedCurrentVersionId: options.expectedCurrentVersion ?? null, idempotencyKey: options.idempotencyKey });
112476
113360
  const result2 = action === "publish" || action === "resume" ? await continuePrivatePublication(client2, directory2, { confirm: true, waitMs: Number(options.waitSeconds) * 1000 }) : await inspectPrivatePublication(client2, directory2, action === "cancel");
112477
113361
  print2(result2, options.json);
112478
113362
  } catch (error61) {
@@ -112574,7 +113458,7 @@ __export(exports_auth, {
112574
113458
  import { createInterface as createInterface3 } from "readline";
112575
113459
  function prompt(question) {
112576
113460
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
112577
- return new Promise((resolve28) => {
113461
+ return new Promise((resolve29) => {
112578
113462
  let settled = false;
112579
113463
  const finish = (answer) => {
112580
113464
  if (settled)
@@ -112583,7 +113467,7 @@ function prompt(question) {
112583
113467
  rl.close();
112584
113468
  if (answer === null)
112585
113469
  process.exitCode = 130;
112586
- resolve28(answer);
113470
+ resolve29(answer);
112587
113471
  };
112588
113472
  rl.once("SIGINT", () => finish(null));
112589
113473
  rl.once("close", () => finish(null));
@@ -112761,7 +113645,7 @@ Skipped. Run it any time: skills setup agents`));
112761
113645
  function promptHidden(question) {
112762
113646
  const stdin = process.stdin;
112763
113647
  process.stdout.write(question);
112764
- return new Promise((resolve28) => {
113648
+ return new Promise((resolve29) => {
112765
113649
  let value = "";
112766
113650
  const done = (answer) => {
112767
113651
  stdin.off("data", onData);
@@ -112771,7 +113655,7 @@ function promptHidden(question) {
112771
113655
  `);
112772
113656
  if (answer === null)
112773
113657
  process.exitCode = 130;
112774
- resolve28(answer);
113658
+ resolve29(answer);
112775
113659
  };
112776
113660
  const onData = (chunk2) => {
112777
113661
  for (const character of chunk2.toString()) {
@@ -113317,7 +114201,7 @@ function promptInvitationProof(kind) {
113317
114201
  const label = kind === "token" ? "invitation token" : "recovery code";
113318
114202
  const character = kind === "token" ? /^[A-Za-z0-9_-]$/ : /^\d$/;
113319
114203
  const { stdin, stderr: output2 } = process, wasRaw = stdin.isRaw, wasFlowing = stdin.readableFlowing;
113320
- return new Promise((resolve28) => {
114204
+ return new Promise((resolve29) => {
113321
114205
  let value = "", settled = false, overflow = false;
113322
114206
  const finish = (answer) => {
113323
114207
  if (settled)
@@ -113334,7 +114218,7 @@ function promptInvitationProof(kind) {
113334
114218
  `);
113335
114219
  if (answer === null)
113336
114220
  process.exitCode = 130;
113337
- resolve28(answer);
114221
+ resolve29(answer);
113338
114222
  };
113339
114223
  const cancel = () => finish(null);
113340
114224
  const keypress = (text4, key) => {
@@ -113906,8 +114790,8 @@ var init_storage2 = __esm(() => {
113906
114790
  });
113907
114791
 
113908
114792
  // src/lib/registry-reconcile.ts
113909
- import { existsSync as existsSync37, readFileSync as readFileSync44, statSync as statSync24, writeFileSync as writeFileSync31 } from "fs";
113910
- import { join as join63 } from "path";
114793
+ import { existsSync as existsSync38, readFileSync as readFileSync45, statSync as statSync24, writeFileSync as writeFileSync32 } from "fs";
114794
+ import { join as join64 } from "path";
113911
114795
  function isDirectory2(path) {
113912
114796
  try {
113913
114797
  return statSync24(path).isDirectory();
@@ -113918,15 +114802,15 @@ function isDirectory2(path) {
113918
114802
  function migrationNeeded(options) {
113919
114803
  if (options.rootDir)
113920
114804
  return false;
113921
- const appDir = options.homeDir ? join63(options.homeDir, ".hasna", "skills") : getDataDir();
113922
- return !(isOwnerLayoutMigrated(appDir) && isDirectory2(join63(appDir, SKILLS_CACHE_DIRNAME)));
114805
+ const appDir = options.homeDir ? join64(options.homeDir, ".hasna", "skills") : getDataDir();
114806
+ return !(isOwnerLayoutMigrated(appDir) && isDirectory2(join64(appDir, SKILLS_CACHE_DIRNAME)));
113923
114807
  }
113924
114808
  function readBaseline(skillDir) {
113925
- const markerPath = join63(skillDir, PULL_MARKER_FILE);
113926
- if (!existsSync37(markerPath))
114809
+ const markerPath = join64(skillDir, PULL_MARKER_FILE);
114810
+ if (!existsSync38(markerPath))
113927
114811
  return;
113928
114812
  try {
113929
- const marker = JSON.parse(readFileSync44(markerPath, "utf-8"));
114813
+ const marker = JSON.parse(readFileSync45(markerPath, "utf-8"));
113930
114814
  if (!isSkillsOwnershipMarker(marker))
113931
114815
  return;
113932
114816
  return {
@@ -113938,11 +114822,11 @@ function readBaseline(skillDir) {
113938
114822
  }
113939
114823
  }
113940
114824
  function readCursor(root2) {
113941
- const path = join63(root2, SYNC_CURSOR_FILE);
113942
- if (!existsSync37(path))
114825
+ const path = join64(root2, SYNC_CURSOR_FILE);
114826
+ if (!existsSync38(path))
113943
114827
  return { runCount: 0 };
113944
114828
  try {
113945
- const cursor2 = JSON.parse(readFileSync44(path, "utf-8"));
114829
+ const cursor2 = JSON.parse(readFileSync45(path, "utf-8"));
113946
114830
  return { runCount: typeof cursor2.runCount === "number" ? cursor2.runCount : 0 };
113947
114831
  } catch {
113948
114832
  return { runCount: 0 };
@@ -113951,12 +114835,12 @@ function readCursor(root2) {
113951
114835
  function resolveCorpusRootReadOnly(options) {
113952
114836
  if (options.rootDir)
113953
114837
  return { root: options.rootDir, migrationPending: false };
113954
- const appDir = options.homeDir ? join63(options.homeDir, ".hasna", "skills") : getDataDirReadOnly();
113955
- const cache3 = join63(appDir, SKILLS_CACHE_DIRNAME);
114838
+ const appDir = options.homeDir ? join64(options.homeDir, ".hasna", "skills") : getDataDirReadOnly();
114839
+ const cache3 = join64(appDir, SKILLS_CACHE_DIRNAME);
113956
114840
  if (isOwnerLayoutMigrated(appDir) && isDirectory2(cache3)) {
113957
114841
  return { root: cache3, migrationPending: false };
113958
114842
  }
113959
- return { root: join63(appDir, INSTALLED_SKILLS_DIRNAME), migrationPending: true };
114843
+ return { root: join64(appDir, INSTALLED_SKILLS_DIRNAME), migrationPending: true };
113960
114844
  }
113961
114845
  function remoteRowToSkill(record11) {
113962
114846
  const slug2 = typeof record11.slug === "string" ? record11.slug : typeof record11.name === "string" ? record11.name : undefined;
@@ -113970,7 +114854,7 @@ function remoteRowToSkill(record11) {
113970
114854
  }
113971
114855
  function recheckLocalSide(plannedLocal, localDir, ops = {
113972
114856
  pack: (dir) => packSkillBundle(dir).sha256,
113973
- exists: existsSync37
114857
+ exists: existsSync38
113974
114858
  }) {
113975
114859
  let localNow;
113976
114860
  try {
@@ -114090,7 +114974,7 @@ async function reconcileRegistry(options = {}) {
114090
114974
  for (const slug2 of allSlugs) {
114091
114975
  const local = locals.get(slug2);
114092
114976
  const remote = remotes.get(slug2);
114093
- const baseline = local ? readBaseline(join63(root2, slug2)) : undefined;
114977
+ const baseline = local ? readBaseline(join64(root2, slug2)) : undefined;
114094
114978
  const { state, reason } = classifySkill(local, remote, baseline);
114095
114979
  let { action, reason: actionReason } = resolveAction(state, direction, conflict);
114096
114980
  if (state === "remote-only" && isDigestless(remote)) {
@@ -114149,7 +115033,7 @@ async function reconcileRegistry(options = {}) {
114149
115033
  try {
114150
115034
  await pushSkill(slug2, { rootDir: root2, client: client2 });
114151
115035
  const pushed = locals.get(slug2);
114152
- writePullMarker(join63(root2, slug2), {
115036
+ writePullMarker(join64(root2, slug2), {
114153
115037
  skill: slug2,
114154
115038
  ...pushed?.version ? { version: pushed.version } : {},
114155
115039
  ...pushed?.sha256 ? { contentHash: pushed.sha256 } : {},
@@ -114221,7 +115105,7 @@ async function reconcileRegistry(options = {}) {
114221
115105
  runCount: readCursor(root2).runCount + 1,
114222
115106
  summary
114223
115107
  };
114224
- writeFileSync31(join63(root2, SYNC_CURSOR_FILE), `${JSON.stringify(cursor2, null, 2)}
115108
+ writeFileSync32(join64(root2, SYNC_CURSOR_FILE), `${JSON.stringify(cursor2, null, 2)}
114225
115109
  `);
114226
115110
  return {
114227
115111
  corpusRoot: root2,