@hasna/skills 0.10.8 → 0.10.9

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.9",
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,860 @@ 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
+ for (const rawDir of pathInput.split(delimiter).filter(Boolean)) {
107444
+ const dir = resolve22(rawDir);
107445
+ for (const [name, target] of Object.entries(current.bins)) {
107446
+ const path = join54(dir, name);
107447
+ if (!existsSync33(path))
107448
+ continue;
107449
+ assertNoSymlinkAncestors(dir);
107450
+ let stat3;
107451
+ try {
107452
+ stat3 = lstatSync27(path);
107453
+ } catch {
107454
+ continue;
107455
+ }
107456
+ if (!stat3.isSymbolicLink())
107457
+ continue;
107458
+ let actual;
107459
+ try {
107460
+ actual = realpathSync11(path);
107461
+ } catch {
107462
+ throw new Error("LAUNCHER_TARGET_UNREADABLE");
107463
+ }
107464
+ if (actual !== join54(currentPackageRoot, target))
107465
+ continue;
107466
+ const oldLinkTarget = readlinkSync4(path);
107467
+ if (resolve22(dirname29(path), oldLinkTarget) !== actual)
107468
+ throw new Error("LAUNCHER_SYMLINK_CHAIN_UNSUPPORTED");
107469
+ const newTarget = join54(runtimeRoot, "__target__", "node_modules", "@hasna", "skills", target);
107470
+ launchers.push({ path, oldTarget: actual, oldLinkTarget, newTarget, backupPath: `${path}.skills-prev-${randomUUID9()}` });
107471
+ }
107472
+ }
107473
+ if (!launchers.some((item) => item.path === launcherPath))
107474
+ throw new Error("ACTIVE_LAUNCHER_NOT_IN_PATH_INVENTORY");
107475
+ if (new Set(launchers.map((item) => item.path)).size !== launchers.length)
107476
+ throw new Error("LAUNCHER_PATH_DUPLICATE");
107477
+ return { home, runtimeRoot, currentPackageRoot, currentVersion: current.version, bin: current.bins, launchers };
107478
+ }
107479
+ function verifyIntegrity(bytes, integrity) {
107480
+ const match = /^sha512-([A-Za-z0-9+/]+={0,2})$/.exec(integrity);
107481
+ if (!match)
107482
+ throw new Error("REGISTRY_INTEGRITY_UNSUPPORTED");
107483
+ const actual = createHash26("sha512").update(bytes).digest("base64");
107484
+ if (actual !== match[1])
107485
+ throw new Error("TARBALL_INTEGRITY_MISMATCH");
107486
+ }
107487
+ function expectedTarballUrl(input2, origin, version2) {
107488
+ if (typeof input2 !== "string")
107489
+ throw new Error("REGISTRY_TARBALL_URL_INVALID");
107490
+ let url2;
107491
+ try {
107492
+ url2 = new URL(input2);
107493
+ } catch {
107494
+ throw new Error("REGISTRY_TARBALL_URL_INVALID");
107495
+ }
107496
+ const base3 = new URL(origin);
107497
+ if (url2.protocol !== "https:" && base3.protocol === "https:")
107498
+ throw new Error("REGISTRY_TARBALL_URL_INVALID");
107499
+ if (url2.origin !== base3.origin || url2.username || url2.password || url2.search || url2.hash)
107500
+ throw new Error("REGISTRY_TARBALL_URL_INVALID");
107501
+ if (!url2.pathname.endsWith(`/skills-${version2}.tgz`))
107502
+ throw new Error("REGISTRY_TARBALL_URL_INVALID");
107503
+ return url2.toString();
107504
+ }
107505
+ function tarField(block, start, length) {
107506
+ const field2 = block.subarray(start, start + length);
107507
+ const end = field2.indexOf(0);
107508
+ return new TextDecoder("utf-8", { fatal: true }).decode(end < 0 ? field2 : field2.subarray(0, end));
107509
+ }
107510
+ function tarOctal2(block, start, length) {
107511
+ const raw = new TextDecoder().decode(block.subarray(start, start + length)).replace(/[\0 ]+$/g, "").trim();
107512
+ if (!raw)
107513
+ return 0;
107514
+ if (!/^[0-7]+$/.test(raw))
107515
+ throw new Error("TARBALL_TAR_NUMERIC_FIELD_INVALID");
107516
+ const value = Number.parseInt(raw, 8);
107517
+ if (!Number.isSafeInteger(value))
107518
+ throw new Error("TARBALL_TAR_NUMERIC_FIELD_INVALID");
107519
+ return value;
107520
+ }
107521
+ async function gunzipCapped(bytes, limit) {
107522
+ if (bytes[0] !== 31 || bytes[1] !== 139)
107523
+ throw new Error("TARBALL_GZIP_REQUIRED");
107524
+ const stream = Readable.from([Buffer.from(bytes)]).pipe(createGunzip2());
107525
+ const chunks = [];
107526
+ let size2 = 0;
107527
+ try {
107528
+ for await (const chunk2 of stream) {
107529
+ const part = Buffer.from(chunk2);
107530
+ size2 += part.byteLength;
107531
+ if (size2 > limit) {
107532
+ stream.destroy();
107533
+ throw new Error("TARBALL_EXPANDED_SIZE_INVALID");
107534
+ }
107535
+ chunks.push(part);
107536
+ }
107537
+ } finally {
107538
+ stream.destroy();
107539
+ }
107540
+ return Buffer.concat(chunks, size2);
107541
+ }
107542
+ async function preflightTarball(bytes, limits = {}) {
107543
+ const expanded = await gunzipCapped(bytes, limits.maxExpandedBytes ?? MAX_TARBALL_EXPANDED_BYTES);
107544
+ const entries = [];
107545
+ const paths = new Set;
107546
+ let offset = 0, ended = false, fileBytes = 0;
107547
+ while (offset + 512 <= expanded.byteLength) {
107548
+ const block = expanded.subarray(offset, offset + 512);
107549
+ if (block.every((byte) => byte === 0)) {
107550
+ ended = true;
107551
+ break;
107552
+ }
107553
+ if (entries.length >= (limits.maxEntries ?? MAX_TARBALL_ENTRIES))
107554
+ throw new Error("TARBALL_ENTRY_COUNT_INVALID");
107555
+ const expectedChecksum = tarOctal2(block, 148, 8);
107556
+ let checksum = 0;
107557
+ for (let i = 0;i < 512; i++)
107558
+ checksum += i >= 148 && i < 156 ? 32 : block[i];
107559
+ if (checksum !== expectedChecksum)
107560
+ throw new Error("TARBALL_TAR_CHECKSUM_INVALID");
107561
+ const name = tarField(block, 0, 100), prefix = tarField(block, 345, 155);
107562
+ const rawPath = `${prefix ? `${prefix}/` : ""}${name}`.replace(/\/$/, "");
107563
+ const pathParts2 = rawPath.split("/");
107564
+ if (rawPath !== "package" && !rawPath.startsWith("package/") || rawPath.startsWith("/") || rawPath.includes("\\") || pathParts2.some((part) => !part || part === "." || part === ".."))
107565
+ throw new Error("TARBALL_PATH_INVALID");
107566
+ if (paths.has(rawPath))
107567
+ throw new Error("TARBALL_DUPLICATE_PATH");
107568
+ paths.add(rawPath);
107569
+ const flag = block[156] === 0 ? "0" : String.fromCharCode(block[156]);
107570
+ const size2 = tarOctal2(block, 124, 12);
107571
+ const kind = flag === "0" ? "file" : flag === "5" ? "directory" : null;
107572
+ if (!kind || kind === "directory" && size2 !== 0 || rawPath === "package" && kind !== "directory")
107573
+ throw new Error("TARBALL_ENTRY_TYPE_UNSUPPORTED");
107574
+ fileBytes += size2;
107575
+ if (fileBytes > (limits.maxExpandedBytes ?? MAX_TARBALL_EXPANDED_BYTES))
107576
+ throw new Error("TARBALL_EXPANDED_SIZE_INVALID");
107577
+ entries.push({ path: rawPath, kind, size: size2 });
107578
+ const padded = Math.ceil(size2 / 512) * 512;
107579
+ offset += 512 + padded;
107580
+ if (offset > expanded.byteLength)
107581
+ throw new Error("TARBALL_TAR_TRUNCATED");
107582
+ }
107583
+ if (!ended || !expanded.subarray(offset).every((byte) => byte === 0))
107584
+ throw new Error("TARBALL_TAR_TERMINATOR_INVALID");
107585
+ return { entries, expandedBytes: expanded.byteLength };
107586
+ }
107587
+ async function downloadPackage(version2, stagingRoot, registryOrigin, fetcher) {
107588
+ const metadataUrl = `${registryOrigin}/@hasna%2fskills/${encodeURIComponent(version2)}`;
107589
+ const timeout = AbortSignal.timeout(30000);
107590
+ const metadataResponse = await fetcher(metadataUrl, { redirect: "error", signal: timeout });
107591
+ if (!metadataResponse.ok || new URL(metadataResponse.url || metadataUrl).origin !== new URL(registryOrigin).origin)
107592
+ throw new Error("REGISTRY_METADATA_FETCH_FAILED");
107593
+ const metadata = object8(JSON.parse(new TextDecoder().decode(await readBodyCapped(metadataResponse, 5 * 1024 * 1024, "REGISTRY_METADATA_SIZE_INVALID"))), "REGISTRY_METADATA_INVALID");
107594
+ if (metadata.name !== PACKAGE_NAME || metadata.version !== version2 || metadata._id !== `${PACKAGE_NAME}@${version2}`)
107595
+ throw new Error("REGISTRY_PACKAGE_IDENTITY_MISMATCH");
107596
+ const dist = object8(metadata.dist, "REGISTRY_DIST_INVALID");
107597
+ const integrity = typeof dist.integrity === "string" ? dist.integrity : "";
107598
+ const tarballUrl = expectedTarballUrl(dist.tarball, registryOrigin, version2);
107599
+ const response = await fetcher(tarballUrl, { redirect: "error", signal: AbortSignal.timeout(30000) });
107600
+ if (!response.ok || new URL(response.url || tarballUrl).origin !== new URL(registryOrigin).origin)
107601
+ throw new Error("REGISTRY_TARBALL_FETCH_FAILED");
107602
+ const bytes = await readBodyCapped(response, MAX_TARBALL_BYTES, "TARBALL_SIZE_INVALID");
107603
+ if (!bytes.byteLength)
107604
+ throw new Error("TARBALL_SIZE_INVALID");
107605
+ verifyIntegrity(bytes, integrity);
107606
+ const preflight = await preflightTarball(bytes);
107607
+ const archive = new Bun.Archive(bytes);
107608
+ const files = await archive.files();
107609
+ const regularFiles = preflight.entries.filter((entry) => entry.kind === "file");
107610
+ if (files.size !== regularFiles.length || regularFiles.some((entry) => files.get(entry.path)?.size !== entry.size))
107611
+ throw new Error("TARBALL_ARCHIVE_INTERPRETATION_MISMATCH");
107612
+ const packageJson = files.get("package/package.json");
107613
+ if (!packageJson)
107614
+ throw new Error("TARBALL_PACKAGE_JSON_MISSING");
107615
+ const unpacked = object8(JSON.parse(await packageJson.text()), "TARBALL_PACKAGE_JSON_INVALID");
107616
+ if (unpacked.name !== PACKAGE_NAME || unpacked.version !== version2)
107617
+ throw new Error("TARBALL_PACKAGE_IDENTITY_MISMATCH");
107618
+ const bins = packageBins(unpacked);
107619
+ const archiveRoot = join54(stagingRoot, "verified-archive");
107620
+ mkdirSync24(archiveRoot, { mode: 448 });
107621
+ await archive.extract(archiveRoot);
107622
+ const extractedPackage = join54(archiveRoot, "package");
107623
+ const extracted = readTree(extractedPackage, false);
107624
+ const tarballPath = join54(stagingRoot, "verified-package.tgz");
107625
+ writeFileSync23(tarballPath, bytes, { flag: "wx", mode: 384 });
107626
+ if (!readFileSync37(tarballPath).equals(Buffer.from(bytes)))
107627
+ throw new Error("TARBALL_READBACK_MISMATCH");
107628
+ return {
107629
+ tarballPath,
107630
+ tarballUrl,
107631
+ integrity,
107632
+ sha256: hash5(bytes),
107633
+ byteSize: bytes.byteLength,
107634
+ packageTreeSha256: extracted.digest,
107635
+ packageBins: bins
107636
+ };
107637
+ }
107638
+ async function readBodyCapped(response, limit, errorCode) {
107639
+ if (!response.body)
107640
+ throw new Error("HTTP_RESPONSE_BODY_MISSING");
107641
+ const reader = response.body.getReader();
107642
+ const chunks = [];
107643
+ let size2 = 0;
107644
+ try {
107645
+ for (;; ) {
107646
+ const { done, value } = await reader.read();
107647
+ if (done)
107648
+ break;
107649
+ size2 += value.byteLength;
107650
+ if (size2 > limit) {
107651
+ await reader.cancel();
107652
+ throw new Error(errorCode);
107653
+ }
107654
+ chunks.push(value);
107655
+ }
107656
+ } finally {
107657
+ reader.releaseLock();
107658
+ }
107659
+ const bytes = new Uint8Array(size2);
107660
+ let offset = 0;
107661
+ for (const chunk2 of chunks) {
107662
+ bytes.set(chunk2, offset);
107663
+ offset += chunk2.byteLength;
107664
+ }
107665
+ return bytes;
107666
+ }
107667
+ function readTree(root2, includeModes) {
107668
+ const base3 = realpathSync11(root2);
107669
+ const entries = [];
107670
+ const walk2 = (dir, prefix = "") => {
107671
+ for (const item of readdirSync20(dir).sort()) {
107672
+ const path = join54(dir, item);
107673
+ const rel = prefix ? `${prefix}/${item}` : item;
107674
+ const stat3 = lstatSync27(path);
107675
+ if (stat3.isSymbolicLink()) {
107676
+ const link2 = readlinkSync4(path);
107677
+ const resolved = resolve22(dirname29(path), link2);
107678
+ if (!isWithin(base3, resolved))
107679
+ throw new Error("TREE_SYMLINK_ESCAPES_ROOT");
107680
+ let physicalTarget;
107681
+ try {
107682
+ physicalTarget = realpathSync11(path);
107683
+ } catch {
107684
+ throw new Error("TREE_SYMLINK_TARGET_MISSING");
107685
+ }
107686
+ if (!isWithin(base3, physicalTarget))
107687
+ throw new Error("TREE_SYMLINK_ESCAPES_ROOT");
107688
+ entries.push({ path: rel, kind: "symlink", target: link2, ...includeModes ? { mode: stat3.mode & 511 } : {} });
107689
+ } else if (stat3.isDirectory()) {
107690
+ entries.push({ path: rel, kind: "directory", ...includeModes ? { mode: stat3.mode & 511 } : {} });
107691
+ walk2(path, rel);
107692
+ } else if (stat3.isFile()) {
107693
+ if (stat3.nlink !== 1)
107694
+ throw new Error("TREE_HARDLINK_UNSUPPORTED");
107695
+ const bytes = readFileSync37(path);
107696
+ entries.push({ path: rel, kind: "file", sha256: hash5(bytes), ...includeModes ? { mode: stat3.mode & 511 } : {} });
107697
+ } else
107698
+ throw new Error("TREE_ENTRY_TYPE_UNSUPPORTED");
107699
+ }
107700
+ };
107701
+ walk2(base3);
107702
+ return { entries, digest: hash5(new TextEncoder().encode(JSON.stringify(entries))) };
107703
+ }
107704
+ function normalizeModes(root2) {
107705
+ const walk2 = (dir) => {
107706
+ for (const item of readdirSync20(dir)) {
107707
+ const path = join54(dir, item);
107708
+ const stat3 = lstatSync27(path);
107709
+ if (stat3.isSymbolicLink())
107710
+ continue;
107711
+ if (stat3.isDirectory()) {
107712
+ chmodSync4(path, stat3.mode & 493);
107713
+ walk2(path);
107714
+ } else if (stat3.isFile()) {
107715
+ chmodSync4(path, stat3.mode & 493);
107716
+ } else
107717
+ throw new Error("RUNTIME_ENTRY_TYPE_UNSUPPORTED");
107718
+ }
107719
+ };
107720
+ walk2(root2);
107721
+ }
107722
+ function writeJsonPrivate(path, value) {
107723
+ const bytes = Buffer.from(`${JSON.stringify(value, null, 2)}
107724
+ `);
107725
+ writeFileSync23(path, bytes, { flag: "wx", mode: 384 });
107726
+ chmodSync4(path, 384);
107727
+ if (!readFileSync37(path).equals(bytes))
107728
+ throw new Error("PRIVATE_JSON_READBACK_MISMATCH");
107729
+ }
107730
+ function writePreimageManifest(stage, configs, launchers) {
107731
+ const path = join54(stage, "preimage", "manifest.json");
107732
+ const value = { schema: "skills.copyfile-preimage.v1", configs, launchers };
107733
+ const bytes = Buffer.from(`${JSON.stringify(value, null, 2)}
107734
+ `);
107735
+ writeFileSync23(path, bytes, { flag: "wx", mode: 256 });
107736
+ chmodSync4(path, 256);
107737
+ if (!readFileSync37(path).equals(bytes))
107738
+ throw new Error("PREIMAGE_MANIFEST_READBACK_MISMATCH");
107739
+ return hash5(bytes);
107740
+ }
107741
+ function verifyPreimageManifest(runtimePath, receipt, home) {
107742
+ const path = join54(runtimePath, "preimage", "manifest.json");
107743
+ const bytes = readFileSync37(path);
107744
+ if (hash5(bytes) !== receipt.preimageSha256)
107745
+ throw new Error("PREIMAGE_MANIFEST_DRIFT");
107746
+ const manifest = object8(JSON.parse(bytes.toString("utf8")), "PREIMAGE_MANIFEST_INVALID");
107747
+ const expected = { schema: "skills.copyfile-preimage.v1", configs: receipt.configs, launchers: receipt.launchers };
107748
+ if (JSON.stringify(manifest) !== JSON.stringify(expected))
107749
+ throw new Error("PREIMAGE_RECEIPT_MISMATCH");
107750
+ if (receipt.configs.length !== CONFIG_PREIMAGE_PATHS.length)
107751
+ throw new Error("PREIMAGE_CONFIG_SET_INVALID");
107752
+ for (const [index, relativePath] of CONFIG_PREIMAGE_PATHS.entries()) {
107753
+ const config2 = receipt.configs[index];
107754
+ if (config2.relativePath !== relativePath || config2.sourcePath !== join54(home, relativePath))
107755
+ throw new Error("PREIMAGE_CONFIG_BINDING_INVALID");
107756
+ if (config2.present) {
107757
+ const backup = join54(runtimePath, "preimage", "configs", relativePath);
107758
+ const stat3 = lstatSync27(backup);
107759
+ const saved = readFileSync37(backup);
107760
+ if (!stat3.isFile() || stat3.isSymbolicLink() || hash5(saved) !== config2.sha256 || saved.byteLength !== config2.byteSize)
107761
+ throw new Error("PREIMAGE_CONFIG_DRIFT");
107762
+ }
107763
+ }
107764
+ }
107765
+ function captureConfigPreimages(home, stage) {
107766
+ const root2 = join54(stage, "preimage", "configs");
107767
+ mkdirSync24(root2, { recursive: true, mode: 448 });
107768
+ return CONFIG_PREIMAGE_PATHS.map((relativePath) => {
107769
+ const sourcePath = join54(home, relativePath);
107770
+ if (!entryExists(sourcePath))
107771
+ return { sourcePath, relativePath, present: false };
107772
+ const stat3 = lstatSync27(sourcePath);
107773
+ if (!stat3.isFile() || stat3.isSymbolicLink())
107774
+ throw new Error("CONFIG_PREIMAGE_NOT_REGULAR_FILE");
107775
+ const bytes = readFileSync37(sourcePath);
107776
+ const backupPath = join54(root2, relativePath);
107777
+ mkdirSync24(dirname29(backupPath), { recursive: true, mode: 448 });
107778
+ writeFileSync23(backupPath, bytes, { flag: "wx", mode: 384 });
107779
+ chmodSync4(backupPath, 384);
107780
+ const readback = readFileSync37(backupPath);
107781
+ if (!readback.equals(bytes))
107782
+ throw new Error("CONFIG_PREIMAGE_READBACK_MISMATCH");
107783
+ return { sourcePath, relativePath, present: true, sha256: hash5(bytes), byteSize: bytes.byteLength };
107784
+ });
107785
+ }
107786
+ function assertConfigUnchanged(configs) {
107787
+ for (const config2 of configs) {
107788
+ const present = entryExists(config2.sourcePath);
107789
+ if (present !== config2.present)
107790
+ throw new Error("CONFIG_DRIFT_DURING_UPDATE");
107791
+ if (present) {
107792
+ const stat3 = lstatSync27(config2.sourcePath);
107793
+ if (!stat3.isFile() || stat3.isSymbolicLink() || hash5(readFileSync37(config2.sourcePath)) !== config2.sha256) {
107794
+ throw new Error("CONFIG_DRIFT_DURING_UPDATE");
107795
+ }
107796
+ }
107797
+ }
107798
+ }
107799
+ async function runNpm(args2, cwd2, home, staging) {
107800
+ const userNpmrc = join54(staging, "npm-user.npmrc"), globalNpmrc = join54(staging, "npm-global.npmrc");
107801
+ if (!existsSync33(userNpmrc))
107802
+ writeFileSync23(userNpmrc, `registry=${REGISTRY_ORIGIN}/
107803
+ ignore-scripts=true
107804
+ `, { mode: 384, flag: "wx" });
107805
+ if (!existsSync33(globalNpmrc))
107806
+ writeFileSync23(globalNpmrc, `registry=${REGISTRY_ORIGIN}/
107807
+ ignore-scripts=true
107808
+ `, { mode: 384, flag: "wx" });
107809
+ const env3 = {
107810
+ PATH: process.env.PATH ?? "",
107811
+ HOME: home,
107812
+ TMPDIR: staging,
107813
+ TMP: staging,
107814
+ TEMP: staging,
107815
+ CI: "1",
107816
+ NPM_CONFIG_USERCONFIG: userNpmrc,
107817
+ NPM_CONFIG_GLOBALCONFIG: globalNpmrc,
107818
+ NPM_CONFIG_CACHE: join54(staging, "npm-cache"),
107819
+ NPM_CONFIG_REGISTRY: `${REGISTRY_ORIGIN}/`,
107820
+ NPM_CONFIG_IGNORE_SCRIPTS: "true"
107821
+ };
107822
+ const proc = Bun.spawn(["npm", ...args2, "--registry", `${REGISTRY_ORIGIN}/`, "--ignore-scripts", "--no-audit", "--no-fund"], {
107823
+ cwd: cwd2,
107824
+ env: env3,
107825
+ stdin: "ignore",
107826
+ stdout: "ignore",
107827
+ stderr: "ignore"
107828
+ });
107829
+ const exitCode = await proc.exited;
107830
+ if (exitCode !== 0)
107831
+ throw new Error("DEPENDENCY_GRAPH_INSTALL_FAILED");
107832
+ }
107833
+ function comparePackageTrees(expectedRoot, installedRoot) {
107834
+ const expected = readTree(expectedRoot, false);
107835
+ const installed = readTree(installedRoot, false);
107836
+ if (JSON.stringify(expected.entries) !== JSON.stringify(installed.entries))
107837
+ throw new Error("INSTALLED_PACKAGE_BYTES_MISMATCH");
107838
+ return expected.digest;
107839
+ }
107840
+ function replaceLink(path, target, backupPath, oldLinkTarget) {
107841
+ const stat3 = lstatSync27(path);
107842
+ if (!stat3.isSymbolicLink() || entryExists(backupPath))
107843
+ throw new Error("LAUNCHER_PREIMAGE_DRIFT");
107844
+ const temp = `${path}.skills-next-${randomUUID9()}`;
107845
+ symlinkSync(oldLinkTarget, backupPath);
107846
+ if (readlinkSync4(backupPath) !== oldLinkTarget || realpathSync11(backupPath) !== resolve22(dirname29(path), oldLinkTarget))
107847
+ throw new Error("LAUNCHER_BACKUP_READBACK_MISMATCH");
107848
+ symlinkSync(target, temp);
107849
+ if (realpathSync11(temp) !== target)
107850
+ throw new Error("LAUNCHER_NEW_LINK_READBACK_MISMATCH");
107851
+ renameSync13(temp, path);
107852
+ }
107853
+ function atomicJson(path, value) {
107854
+ const temp = `${path}.next-${randomUUID9()}`;
107855
+ const bytes = Buffer.from(`${JSON.stringify(value, null, 2)}
107856
+ `);
107857
+ writeFileSync23(temp, bytes, { flag: "wx", mode: 384 });
107858
+ chmodSync4(temp, 384);
107859
+ renameSync13(temp, path);
107860
+ if (!readFileSync37(path).equals(bytes))
107861
+ throw new Error("RECEIPT_READBACK_MISMATCH");
107862
+ }
107863
+ function persistReceipt(path, value) {
107864
+ if (entryExists(path)) {
107865
+ const priorBytes = readFileSync37(path);
107866
+ const prior = object8(JSON.parse(priorBytes.toString("utf8")), "RECEIPT_INVALID");
107867
+ if (prior.id !== value.id || prior.schema !== "skills.copyfile-runtime-receipt.v1" || typeof prior.state !== "string" || !/^[a-z-]{1,32}$/.test(prior.state))
107868
+ throw new Error("RECEIPT_ID_DRIFT");
107869
+ const nextBytes = Buffer.from(`${JSON.stringify(value, null, 2)}
107870
+ `);
107871
+ if (priorBytes.equals(nextBytes))
107872
+ return;
107873
+ const historyRoot = join54(dirname29(path), "receipt-history");
107874
+ if (!entryExists(historyRoot))
107875
+ mkdirSync24(historyRoot, { mode: 448 });
107876
+ const historyStat = lstatSync27(historyRoot);
107877
+ if (!historyStat.isDirectory() || historyStat.isSymbolicLink() || (historyStat.mode & 63) !== 0)
107878
+ throw new Error("RECEIPT_HISTORY_NOT_PRIVATE");
107879
+ const historyPath = join54(historyRoot, `${value.id}-${String(prior.state)}-${randomUUID9()}.json`);
107880
+ writeFileSync23(historyPath, priorBytes, { flag: "wx", mode: 256 });
107881
+ chmodSync4(historyPath, 256);
107882
+ if (!readFileSync37(historyPath).equals(priorBytes))
107883
+ throw new Error("RECEIPT_HISTORY_READBACK_MISMATCH");
107884
+ }
107885
+ atomicJson(path, value);
107886
+ }
107887
+ async function updateCopyfileRuntime(version2, options = {}) {
107888
+ if (!new RegExp(SEMVER_PATTERN).test(version2))
107889
+ throw new Error("EXACT_VERSION_REQUIRED");
107890
+ if (!STABLE_SEMVER_PATTERN.test(version2))
107891
+ throw new Error("EXACT_STABLE_VERSION_REQUIRED");
107892
+ const home = resolve22(options.homeDir ?? process.env.HOME ?? "");
107893
+ if (!home || !existsSync33(home))
107894
+ throw new Error("HOME_NOT_FOUND");
107895
+ const layout = resolveRuntimeLayout(home, options.pathValue ?? process.env.PATH ?? "");
107896
+ if (process.platform === "win32")
107897
+ throw new Error("COPYFILE_RUNTIME_PLATFORM_UNSUPPORTED");
107898
+ const registryOrigin = options.registryOrigin ?? REGISTRY_ORIGIN;
107899
+ const origin = new URL(registryOrigin);
107900
+ if (origin.protocol !== "https:" && options.registryOrigin === undefined)
107901
+ throw new Error("REGISTRY_ORIGIN_INVALID");
107902
+ if (origin.username || origin.password || origin.search || origin.hash)
107903
+ throw new Error("REGISTRY_ORIGIN_INVALID");
107904
+ const runtimeRootStat = lstatSync27(layout.runtimeRoot);
107905
+ if ((runtimeRootStat.mode & 63) !== 0)
107906
+ throw new Error("RUNTIME_ROOT_NOT_PRIVATE");
107907
+ const id2 = randomUUID9();
107908
+ const releaseLock = acquireRuntimeLock(layout.runtimeRoot);
107909
+ const stagePath = join54(layout.runtimeRoot, `.stage-${version2}-${id2}`);
107910
+ const finalPath = join54(layout.runtimeRoot, `${version2}-copyfile`);
107911
+ const launchers = layout.launchers.map((item) => ({ ...item, newTarget: join54(finalPath, "node_modules", "@hasna", "skills", layout.bin[basename13(item.path)]) }));
107912
+ let switched = [];
107913
+ let moved = false;
107914
+ try {
107915
+ if (entryExists(finalPath))
107916
+ throw new Error("TARGET_RUNTIME_ALREADY_EXISTS");
107917
+ mkdirSync24(stagePath, { mode: 448 });
107918
+ chmodSync4(stagePath, 448);
107919
+ const configPreimages = captureConfigPreimages(home, stagePath);
107920
+ const fetcher = options.fetcher ?? fetch;
107921
+ const artifact = await downloadPackage(version2, stagePath, origin.origin, fetcher);
107922
+ if (artifact.packageBins && JSON.stringify(Object.entries(artifact.packageBins).sort()) !== JSON.stringify(Object.entries(layout.bin).sort())) {
107923
+ throw new Error("PACKAGE_BIN_SET_CHANGED");
107924
+ }
107925
+ const stagePackage = join54(stagePath, "install");
107926
+ mkdirSync24(stagePackage, { mode: 448 });
107927
+ copyFileSync(artifact.tarballPath, join54(stagePackage, "verified.tgz"));
107928
+ writeFileSync23(join54(stagePackage, "package.json"), JSON.stringify({ private: true, dependencies: { [PACKAGE_NAME]: "file:./verified.tgz" } }, null, 2), { mode: 384, flag: "wx" });
107929
+ await runNpm(["install", "--package-lock-only", "--prefix", stagePackage], stagePackage, home, stagePath);
107930
+ await runNpm(["ci", "--prefix", stagePackage], stagePackage, home, stagePath);
107931
+ const lockBytes = readFileSync37(join54(stagePackage, "package-lock.json"));
107932
+ const installedPackage = join54(stagePackage, "node_modules", "@hasna", "skills");
107933
+ const installed = readPackage(join54(installedPackage, "package.json"));
107934
+ if (installed.version !== version2 || JSON.stringify(Object.entries(installed.bins).sort()) !== JSON.stringify(Object.entries(layout.bin).sort())) {
107935
+ throw new Error("INSTALLED_PACKAGE_IDENTITY_MISMATCH");
107936
+ }
107937
+ const unpackedPackage = join54(stagePath, "verified-archive", "package");
107938
+ const packageTreeSha256 = comparePackageTrees(unpackedPackage, installedPackage);
107939
+ if (packageTreeSha256 !== artifact.packageTreeSha256)
107940
+ throw new Error("PACKAGE_TREE_HASH_MISMATCH");
107941
+ const nodeModules = join54(stagePackage, "node_modules");
107942
+ normalizeModes(nodeModules);
107943
+ chmodSync4(stagePath, 448);
107944
+ const runtimeTree = readTree(nodeModules, true);
107945
+ if (runtimeTree.entries.some((entry) => entry.kind === "file" && ((entry.mode ?? 0) & 18) !== 0)) {
107946
+ throw new Error("RUNTIME_FILE_MODE_UNSAFE");
107947
+ }
107948
+ assertConfigUnchanged(configPreimages);
107949
+ const targetPackageRoot = join54(finalPath, "node_modules", "@hasna", "skills");
107950
+ const preimageSha256 = writePreimageManifest(stagePath, configPreimages, launchers);
107951
+ const receipt = {
107952
+ schema: "skills.copyfile-runtime-receipt.v1",
107953
+ id: id2,
107954
+ state: "prepared",
107955
+ package: PACKAGE_NAME,
107956
+ currentVersion: layout.currentVersion,
107957
+ targetVersion: version2,
107958
+ registryOrigin: origin.origin,
107959
+ tarballUrl: artifact.tarballUrl,
107960
+ tarballIntegrity: artifact.integrity,
107961
+ tarballSha256: artifact.sha256,
107962
+ tarballBytes: artifact.byteSize,
107963
+ packageTreeSha256,
107964
+ installLockSha256: hash5(lockBytes),
107965
+ runtimeTreeSha256: runtimeTree.digest,
107966
+ runtimeRoot: layout.runtimeRoot,
107967
+ currentPackageRoot: layout.currentPackageRoot,
107968
+ targetPackageRoot,
107969
+ configs: configPreimages,
107970
+ preimageSha256,
107971
+ launchers,
107972
+ switchedLaunchers: [],
107973
+ rollbackCompletedLaunchers: []
107974
+ };
107975
+ writeJsonPrivate(join54(stagePath, "rollout-receipt.json"), receipt);
107976
+ const finalNodeModules = join54(stagePackage, "node_modules");
107977
+ const stagedNodeModules = join54(stagePath, "node_modules");
107978
+ renameSync13(finalNodeModules, stagedNodeModules);
107979
+ copyFileSync(join54(stagePackage, "package-lock.json"), join54(stagePath, "install-lock.json"));
107980
+ chmodSync4(join54(stagePath, "install-lock.json"), 384);
107981
+ const stagedTree = readTree(stagedNodeModules, true);
107982
+ if (stagedTree.digest !== runtimeTree.digest)
107983
+ throw new Error("STAGED_RUNTIME_READBACK_MISMATCH");
107984
+ receipt.targetPackageRoot = join54(finalPath, "node_modules", "@hasna", "skills");
107985
+ receipt.runtimeTreeSha256 = stagedTree.digest;
107986
+ persistReceipt(join54(stagePath, "rollout-receipt.json"), receipt);
107987
+ renameSync13(stagePath, finalPath);
107988
+ moved = true;
107989
+ const finalReceiptBytes = Buffer.from(`${JSON.stringify(receipt, null, 2)}
107990
+ `);
107991
+ if (!readFileSync37(join54(finalPath, "rollout-receipt.json")).equals(finalReceiptBytes))
107992
+ throw new Error("RECEIPT_MOVE_READBACK_MISMATCH");
107993
+ assertConfigUnchanged(configPreimages);
107994
+ for (const item of launchers) {
107995
+ if (realpathSync11(item.path) !== item.oldTarget)
107996
+ throw new Error("LAUNCHER_DRIFT_BEFORE_SWITCH");
107997
+ receipt.state = "switching";
107998
+ receipt.switchedLaunchers = [...receipt.switchedLaunchers, item.path];
107999
+ persistReceipt(join54(finalPath, "rollout-receipt.json"), receipt);
108000
+ replaceLink(item.path, item.newTarget, item.backupPath, item.oldLinkTarget);
108001
+ if (realpathSync11(item.path) !== item.newTarget || realpathSync11(item.backupPath) !== item.oldTarget) {
108002
+ throw new Error("LAUNCHER_SWITCH_READBACK_MISMATCH");
108003
+ }
108004
+ switched.push(item.path);
108005
+ options.onLauncherSwitched?.(item.path);
108006
+ }
108007
+ receipt.state = "switched";
108008
+ persistReceipt(join54(finalPath, "rollout-receipt.json"), receipt);
108009
+ 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 };
108010
+ } catch (error61) {
108011
+ if (switched.length > 0 || moved) {
108012
+ let rollbackFailed = false;
108013
+ for (const item of [...launchers].reverse()) {
108014
+ try {
108015
+ const currentTarget = realpathSync11(item.path);
108016
+ if (currentTarget === item.oldTarget && readlinkSync4(item.path) === item.oldLinkTarget)
108017
+ continue;
108018
+ if (currentTarget !== item.newTarget)
108019
+ throw new Error("LAUNCHER_CHANGED_DURING_ROLLBACK");
108020
+ const temp = `${item.path}.skills-rollback-${id2}`;
108021
+ const afterPath = `${item.path}.skills-after-${id2}`;
108022
+ if (entryExists(afterPath))
108023
+ throw new Error("ROLLBACK_PRESERVATION_PATH_EXISTS");
108024
+ symlinkSync(item.newTarget, afterPath);
108025
+ symlinkSync(item.oldLinkTarget, temp);
108026
+ renameSync13(temp, item.path);
108027
+ } catch {
108028
+ rollbackFailed = true;
108029
+ }
108030
+ }
108031
+ try {
108032
+ const receiptPath = join54(finalPath, "rollout-receipt.json");
108033
+ const receipt = JSON.parse(readFileSync37(receiptPath, "utf8"));
108034
+ receipt.state = rollbackFailed ? "rollback-required" : "rolled-back";
108035
+ persistReceipt(receiptPath, receipt);
108036
+ } catch {
108037
+ rollbackFailed = true;
108038
+ }
108039
+ throw new Error(rollbackFailed ? "UPDATE_FAILED_ROLLBACK_REQUIRED" : "UPDATE_FAILED_ROLLED_BACK");
108040
+ }
108041
+ throw error61 instanceof Error ? error61 : new Error("COPYFILE_UPDATE_FAILED");
108042
+ } finally {
108043
+ releaseLock();
108044
+ }
108045
+ }
108046
+ function rollbackCopyfileRuntime(receiptId, options = {}) {
108047
+ if (!/^[0-9a-f-]{36}$/.test(receiptId))
108048
+ throw new Error("RECEIPT_ID_INVALID");
108049
+ const home = resolve22(options.homeDir ?? process.env.HOME ?? "");
108050
+ if (!home || !existsSync33(home))
108051
+ throw new Error("HOME_NOT_FOUND");
108052
+ const runtimeRoot = join54(realpathSync11(home), ".hasna", "skills", "runtime");
108053
+ assertNoSymlinkAncestors(runtimeRoot);
108054
+ const rootStat = lstatSync27(runtimeRoot);
108055
+ if (!rootStat.isDirectory() || rootStat.isSymbolicLink() || (rootStat.mode & 63) !== 0)
108056
+ throw new Error("RUNTIME_ROOT_NOT_PRIVATE");
108057
+ const releaseLock = acquireRuntimeLock(runtimeRoot);
108058
+ try {
108059
+ const matches2 = readdirSync20(runtimeRoot).filter((name) => {
108060
+ if (!/^\d+\.\d+\.\d+-copyfile$/.test(name))
108061
+ return false;
108062
+ const receipt2 = join54(runtimeRoot, name, "rollout-receipt.json");
108063
+ if (!existsSync33(receipt2))
108064
+ return false;
108065
+ try {
108066
+ return JSON.parse(readFileSync37(receipt2, "utf8")).id === receiptId;
108067
+ } catch {
108068
+ return false;
108069
+ }
108070
+ });
108071
+ if (matches2.length !== 1)
108072
+ throw new Error("RECEIPT_NOT_FOUND_OR_AMBIGUOUS");
108073
+ const receiptPath = join54(runtimeRoot, matches2[0], "rollout-receipt.json");
108074
+ const receipt = object8(JSON.parse(readFileSync37(receiptPath, "utf8")), "RECEIPT_INVALID");
108075
+ if (receipt.schema !== "skills.copyfile-runtime-receipt.v1" || !["switched", "switching", "rollback-required"].includes(receipt.state) || receipt.runtimeRoot !== runtimeRoot) {
108076
+ throw new Error("RECEIPT_STATE_INVALID");
108077
+ }
108078
+ if (receipt.package !== PACKAGE_NAME || matches2[0] !== `${receipt.targetVersion}-copyfile` || !new RegExp(SEMVER_PATTERN).test(receipt.currentVersion) || !new RegExp(SEMVER_PATTERN).test(receipt.targetVersion))
108079
+ throw new Error("RECEIPT_BINDING_INVALID");
108080
+ const runtimePath = join54(runtimeRoot, matches2[0]);
108081
+ if (receipt.targetPackageRoot !== join54(runtimePath, "node_modules", "@hasna", "skills") || receipt.currentPackageRoot !== join54(runtimeRoot, `${receipt.currentVersion}-copyfile`, "node_modules", "@hasna", "skills"))
108082
+ throw new Error("RECEIPT_RUNTIME_BINDING_INVALID");
108083
+ if (readTree(join54(runtimePath, "node_modules"), true).digest !== receipt.runtimeTreeSha256) {
108084
+ throw new Error("ACTIVE_RUNTIME_TREE_DRIFT");
108085
+ }
108086
+ verifyPreimageManifest(runtimePath, receipt, home);
108087
+ const oldBins = readPackage(join54(receipt.currentPackageRoot, "package.json")).bins;
108088
+ const newPackage = readPackage(join54(receipt.targetPackageRoot, "package.json"));
108089
+ if (newPackage.version !== receipt.targetVersion || JSON.stringify(Object.entries(oldBins).sort()) !== JSON.stringify(Object.entries(newPackage.bins).sort()))
108090
+ throw new Error("RECEIPT_BIN_BINDING_INVALID");
108091
+ const switched = [];
108092
+ const seen = new Set;
108093
+ for (const item of receipt.launchers) {
108094
+ const bin = oldBins[basename13(item.path)];
108095
+ 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)
108096
+ throw new Error("RECEIPT_LAUNCHER_BINDING_INVALID");
108097
+ assertNoSymlinkAncestors(dirname29(item.path));
108098
+ seen.add(item.path);
108099
+ const stat3 = lstatSync27(item.path);
108100
+ if (!stat3.isSymbolicLink())
108101
+ throw new Error("LAUNCHER_DRIFT_ROLLBACK_REFUSED");
108102
+ const activeTarget = realpathSync11(item.path);
108103
+ if (activeTarget !== item.newTarget && (activeTarget !== item.oldTarget || readlinkSync4(item.path) !== item.oldLinkTarget))
108104
+ throw new Error("LAUNCHER_DRIFT_ROLLBACK_REFUSED");
108105
+ const hasBackup = entryExists(item.backupPath);
108106
+ if (activeTarget === item.newTarget && !hasBackup)
108107
+ throw new Error("LAUNCHER_PREIMAGE_DRIFT_ROLLBACK_REFUSED");
108108
+ if (hasBackup) {
108109
+ const backup = lstatSync27(item.backupPath);
108110
+ if (!backup.isSymbolicLink() || readlinkSync4(item.backupPath) !== item.oldLinkTarget || realpathSync11(item.backupPath) !== item.oldTarget)
108111
+ throw new Error("LAUNCHER_PREIMAGE_DRIFT_ROLLBACK_REFUSED");
108112
+ }
108113
+ const afterPath = `${item.path}.skills-after-${receiptId}`;
108114
+ if (entryExists(afterPath)) {
108115
+ const after2 = lstatSync27(afterPath);
108116
+ if (!after2.isSymbolicLink() || realpathSync11(afterPath) !== item.newTarget)
108117
+ throw new Error("ROLLBACK_PRESERVATION_DRIFT");
108118
+ }
108119
+ switched.push(item);
108120
+ }
108121
+ for (const item of switched.reverse()) {
108122
+ const activeTarget = realpathSync11(item.path);
108123
+ if (activeTarget === item.oldTarget && readlinkSync4(item.path) === item.oldLinkTarget)
108124
+ continue;
108125
+ receipt.state = "rollback-required";
108126
+ persistReceipt(receiptPath, receipt);
108127
+ const afterPath = `${item.path}.skills-after-${receiptId}`;
108128
+ if (!entryExists(afterPath))
108129
+ symlinkSync(item.newTarget, afterPath);
108130
+ const temp = `${item.path}.skills-rollback-${receiptId}-${randomUUID9()}`;
108131
+ symlinkSync(item.oldLinkTarget, temp);
108132
+ renameSync13(temp, item.path);
108133
+ receipt.rollbackCompletedLaunchers = [...new Set([...receipt.rollbackCompletedLaunchers, item.path])];
108134
+ persistReceipt(receiptPath, receipt);
108135
+ }
108136
+ receipt.state = "rolled-back";
108137
+ persistReceipt(receiptPath, receipt);
108138
+ return { rolledBack: true, receiptId, restoredVersion: receipt.currentVersion, launcherCount: switched.length };
108139
+ } finally {
108140
+ releaseLock();
108141
+ }
108142
+ }
108143
+ 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;
108144
+ var init_runtime_copyfile = __esm(() => {
108145
+ init_skill_contract();
108146
+ STABLE_SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
108147
+ MAX_TARBALL_BYTES = 100 * 1024 * 1024;
108148
+ MAX_TARBALL_EXPANDED_BYTES = 256 * 1024 * 1024;
108149
+ CONFIG_PREIMAGE_PATHS = [
108150
+ ".claude/settings.json",
108151
+ ".codex/config.toml",
108152
+ ".codex/hooks.json",
108153
+ ".hasna/skills/agent-policy.json"
108154
+ ];
108155
+ });
108156
+
107311
108157
  // src/cli/commands/runtime.ts
107312
108158
  var exports_runtime = {};
107313
108159
  __export(exports_runtime, {
107314
108160
  registerRuntime: () => registerRuntime
107315
108161
  });
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";
108162
+ import { createHash as createHash27, randomUUID as randomUUID10 } from "crypto";
108163
+ import { lstatSync as lstatSync28, mkdirSync as mkdirSync25, readFileSync as readFileSync38, realpathSync as realpathSync12, writeFileSync as writeFileSync24 } from "fs";
108164
+ import { basename as basename14, isAbsolute as isAbsolute23, join as join55 } from "path";
107319
108165
  import { createInterface } from "readline";
107320
108166
  function registerRuntime(parent) {
107321
108167
  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 +108200,9 @@ function registerRuntime(parent) {
107354
108200
  if (!expected)
107355
108201
  throw new Error("Cloud artifact not found");
107356
108202
  const bytes = await client2.download(id2, name);
107357
- if (bytes.length !== expected.byteSize || createHash26("sha256").update(bytes).digest("hex") !== expected.sha256)
108203
+ if (bytes.length !== expected.byteSize || createHash27("sha256").update(bytes).digest("hex") !== expected.sha256)
107358
108204
  throw new Error("Cloud artifact integrity verification failed");
107359
- writeFileSync23(options.output, bytes, { flag: "wx", mode: 384 });
108205
+ writeFileSync24(options.output, bytes, { flag: "wx", mode: 384 });
107360
108206
  console.log(JSON.stringify({ path: options.output, sha256: expected.sha256, byteSize: bytes.length }));
107361
108207
  } catch (error61) {
107362
108208
  console.error(JSON.stringify({ error: error61.message }));
@@ -107384,7 +108230,40 @@ function registerRuntime(parent) {
107384
108230
  });
107385
108231
  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
108232
  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) => {
108233
+ 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) => {
108234
+ if (options.version && options.rollback) {
108235
+ console.error(JSON.stringify({ error: "UPDATE_AND_ROLLBACK_ARE_MUTUALLY_EXCLUSIVE" }));
108236
+ process.exitCode = 1;
108237
+ return;
108238
+ }
108239
+ if (options.version) {
108240
+ try {
108241
+ const result2 = await updateCopyfileRuntime(options.version);
108242
+ if (options.json)
108243
+ console.log(JSON.stringify(result2));
108244
+ else
108245
+ console.log(source_default.green(`Updated @hasna/skills to exact version ${options.version} via copyfile runtime.`));
108246
+ } catch (error61) {
108247
+ const message = error61 instanceof Error && /^[A-Z0-9_]+$/.test(error61.message) ? error61.message : "COPYFILE_UPDATE_FAILED";
108248
+ console.error(JSON.stringify({ updated: false, error: message }));
108249
+ process.exitCode = 1;
108250
+ }
108251
+ return;
108252
+ }
108253
+ if (options.rollback) {
108254
+ try {
108255
+ const result2 = rollbackCopyfileRuntime(options.rollback);
108256
+ if (options.json)
108257
+ console.log(JSON.stringify(result2));
108258
+ else
108259
+ console.log(source_default.green(`Restored Skills runtime launchers to ${String(result2.restoredVersion)}.`));
108260
+ } catch (error61) {
108261
+ const message = error61 instanceof Error && /^[A-Z0-9_]+$/.test(error61.message) ? error61.message : "COPYFILE_ROLLBACK_FAILED";
108262
+ console.error(JSON.stringify({ rolledBack: false, error: message }));
108263
+ process.exitCode = 1;
108264
+ }
108265
+ return;
108266
+ }
107388
108267
  if (process.env.SKILLS_TEST_MODE === "1") {
107389
108268
  if (options.json)
107390
108269
  console.log(JSON.stringify({ updated: false, error: "Self-update disabled in test mode" }));
@@ -107442,14 +108321,14 @@ Updating ${name}...
107442
108321
  async function readUpdatedVersion(bunExecutable) {
107443
108322
  const output2 = await readUpdateCommand([bunExecutable, "pm", "bin", "-g"]);
107444
108323
  const globalBin = output2.replace(/\r?\n$/, "");
107445
- if (!globalBin || !isAbsolute22(globalBin) || /[\r\n\0]/.test(globalBin))
108324
+ if (!globalBin || !isAbsolute23(globalBin) || /[\r\n\0]/.test(globalBin))
107446
108325
  throw new Error("Update bin discovery failed");
107447
108326
  const installed = Bun.which("skills", { PATH: globalBin, cwd: globalBin });
107448
108327
  const selected = Bun.which("skills");
107449
108328
  if (!installed || !selected)
107450
108329
  throw new Error("Updated command is unavailable");
107451
- const selectedPath = realpathSync11(selected);
107452
- if (selectedPath !== realpathSync11(installed))
108330
+ const selectedPath = realpathSync12(selected);
108331
+ if (selectedPath !== realpathSync12(installed))
107453
108332
  throw new Error("Updated command is shadowed on PATH");
107454
108333
  const version2 = (await readUpdateCommand([selectedPath, "--version"])).trim();
107455
108334
  if (!new RegExp(SEMVER_PATTERN).test(version2))
@@ -107580,14 +108459,14 @@ function requireHttpUrl(value) {
107580
108459
  }
107581
108460
  function promptLine(question) {
107582
108461
  const rl = createInterface({ input: process.stdin, output: process.stdout });
107583
- return new Promise((resolve22) => {
108462
+ return new Promise((resolve23) => {
107584
108463
  let settled = false;
107585
108464
  const finish = (answer) => {
107586
108465
  if (settled)
107587
108466
  return;
107588
108467
  settled = true;
107589
108468
  rl.close();
107590
- resolve22(answer);
108469
+ resolve23(answer);
107591
108470
  };
107592
108471
  rl.once("SIGINT", () => finish(null));
107593
108472
  rl.once("close", () => finish(null));
@@ -107707,7 +108586,7 @@ async function handleRun(name, args2, options, separatorBeforeSkill) {
107707
108586
  if (!Number.isFinite(timeoutMs) || timeoutMs < 1 || timeoutMs > 1200000 || !Number.isFinite(intervalMs) || intervalMs < 100 || intervalMs > 60000)
107708
108587
  throw new Error("Invalid cloud polling bounds");
107709
108588
  const client3 = await CloudExecutionClient.configured();
107710
- const admitted = await client3.submit(slug2, version2, input2, options.idempotencyKey ?? randomUUID9(), selected?.selection);
108589
+ const admitted = await client3.submit(slug2, version2, input2, options.idempotencyKey ?? randomUUID10(), selected?.selection);
107711
108590
  if (options.wait)
107712
108591
  console.error(JSON.stringify({ executionId: admitted.id, status: admitted.status }));
107713
108592
  const result3 = options.wait ? await client3.wait(admitted.id, { timeoutMs, intervalMs }) : admitted;
@@ -107764,10 +108643,10 @@ async function handleRun(name, args2, options, separatorBeforeSkill) {
107764
108643
  try {
107765
108644
  parsePollingOptions(options);
107766
108645
  inputFiles = (options.file ?? []).map((path) => {
107767
- const info = lstatSync27(path);
108646
+ const info = lstatSync28(path);
107768
108647
  if (!info.isFile() || info.size > 20 * 1024 * 1024)
107769
108648
  throw new Error("Input must be a regular file no larger than 20 MiB");
107770
- return { name: basename13(path), bytes: new Uint8Array(readFileSync37(path)) };
108649
+ return { name: basename14(path), bytes: new Uint8Array(readFileSync38(path)) };
107771
108650
  });
107772
108651
  describeRemoteFiles(inputFiles);
107773
108652
  client2 = new RemoteSkillsClient(routing.apiKey, routing.apiOrigin);
@@ -108145,7 +109024,7 @@ async function handleExportsDownload(runId2, options) {
108145
109024
  const canonicalSkill = typeof remoteRun.skill === "string" ? remoteRun.skill : "remote";
108146
109025
  const requestedSkill = typeof remoteRun.requestedSlug === "string" && remoteRun.requestedSlug.trim() ? remoteRun.requestedSlug : canonicalSkill;
108147
109026
  const exportDir = getRunExportDir(runId2, requestedSkill);
108148
- mkdirSync24(exportDir, { recursive: true });
109027
+ mkdirSync25(exportDir, { recursive: true });
108149
109028
  const downloaded = [];
108150
109029
  for (const artifact of artifacts) {
108151
109030
  const artifactId = String(artifact.id || "");
@@ -108153,9 +109032,9 @@ async function handleExportsDownload(runId2, options) {
108153
109032
  continue;
108154
109033
  const verified = await client2.getVerifiedRunArtifact(runId2, artifactId);
108155
109034
  const relativePath = safeArtifactRelativePath(typeof artifact.relativePath === "string" ? artifact.relativePath : artifact.fileName, String(artifact.fileName || artifactId));
108156
- const outputPath = join54(exportDir, relativePath);
109035
+ const outputPath = join55(exportDir, relativePath);
108157
109036
  ensureSafeExportParent(exportDir, relativePath);
108158
- writeFileSync23(outputPath, verified.bytes, { flag: "wx", mode: 384 });
109037
+ writeFileSync24(outputPath, verified.bytes, { flag: "wx", mode: 384 });
108159
109038
  downloaded.push({ id: artifactId, path: outputPath, byteSize: verified.byteSize });
108160
109039
  }
108161
109040
  const payload = {
@@ -108192,14 +109071,14 @@ function ensureSafeExportParent(root2, relativePath) {
108192
109071
  let parent = root2;
108193
109072
  for (const part of ["", ...parts]) {
108194
109073
  if (part)
108195
- parent = join54(parent, part);
109074
+ parent = join55(parent, part);
108196
109075
  try {
108197
- if (!lstatSync27(parent).isDirectory() || lstatSync27(parent).isSymbolicLink())
109076
+ if (!lstatSync28(parent).isDirectory() || lstatSync28(parent).isSymbolicLink())
108198
109077
  throw new Error("Unsafe artifact directory");
108199
109078
  } catch (error61) {
108200
109079
  if (error61.code !== "ENOENT")
108201
109080
  throw error61;
108202
- mkdirSync24(parent, { mode: 448 });
109081
+ mkdirSync25(parent, { mode: 448 });
108203
109082
  }
108204
109083
  }
108205
109084
  }
@@ -108232,7 +109111,7 @@ async function pollRemoteRun(client2, runId2, options) {
108232
109111
  const remaining = deadline - Date.now();
108233
109112
  if (remaining <= 0)
108234
109113
  break;
108235
- await new Promise((resolve22) => setTimeout(resolve22, Math.min(options.intervalMs, remaining)));
109114
+ await new Promise((resolve23) => setTimeout(resolve23, Math.min(options.intervalMs, remaining)));
108236
109115
  }
108237
109116
  return {
108238
109117
  run: current ?? { id: runId2, status: "queued" },
@@ -108403,6 +109282,7 @@ var init_runtime = __esm(() => {
108403
109282
  init_remote_client();
108404
109283
  init_remote_account2();
108405
109284
  init_remote_files();
109285
+ init_runtime_copyfile();
108406
109286
  });
108407
109287
 
108408
109288
  // src/cli/commands/customer-verification.ts
@@ -108424,7 +109304,7 @@ async function readCode() {
108424
109304
  function promptCode() {
108425
109305
  const { stdin, stderr: output2 } = process;
108426
109306
  const { isRaw: wasRaw, readableFlowing: wasFlowing } = stdin;
108427
- return new Promise((resolve22) => {
109307
+ return new Promise((resolve23) => {
108428
109308
  let value = "", settled = false;
108429
109309
  const finish = (answer) => {
108430
109310
  if (settled)
@@ -108441,7 +109321,7 @@ function promptCode() {
108441
109321
  `);
108442
109322
  if (answer === null)
108443
109323
  process.exitCode = 130;
108444
- resolve22(answer);
109324
+ resolve23(answer);
108445
109325
  };
108446
109326
  const cancel = () => finish(null);
108447
109327
  const keypress = (text4, key) => {
@@ -108486,7 +109366,7 @@ var exports_recurring = {};
108486
109366
  __export(exports_recurring, {
108487
109367
  registerRecurringCommands: () => registerRecurringCommands
108488
109368
  });
108489
- import { constants as constants19, closeSync as closeSync22, fstatSync as fstatSync22, lstatSync as lstatSync28, openSync as openSync22, readSync as readSync16 } from "fs";
109369
+ import { constants as constants19, closeSync as closeSync22, fstatSync as fstatSync22, lstatSync as lstatSync29, openSync as openSync22, readSync as readSync16 } from "fs";
108490
109370
  import { createInterface as createInterface2 } from "readline";
108491
109371
  function readRequest(path) {
108492
109372
  const fd = openSync22(path, constants19.O_RDONLY | constants19.O_NOFOLLOW | constants19.O_NONBLOCK);
@@ -108502,7 +109382,7 @@ function readRequest(path) {
108502
109382
  break;
108503
109383
  length += n2;
108504
109384
  }
108505
- const after2 = fstatSync22(fd), current = lstatSync28(path);
109385
+ const after2 = fstatSync22(fd), current = lstatSync29(path);
108506
109386
  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
109387
  throw new RecurringInputError;
108508
109388
  return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, length)));
@@ -108513,10 +109393,10 @@ function readRequest(path) {
108513
109393
  }
108514
109394
  }
108515
109395
  async function acceptAtTerminal(draft) {
108516
- await new Promise((resolve22, reject2) => process.stderr.write(`${jsonText(draft)}
109396
+ await new Promise((resolve23, reject2) => process.stderr.write(`${jsonText(draft)}
108517
109397
  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) => {
109398
+ `, (error61) => error61 ? reject2(error61) : resolve23()));
109399
+ return new Promise((resolve23) => {
108520
109400
  const rl = createInterface2({ input: process.stdin, output: process.stderr });
108521
109401
  let settled = false;
108522
109402
  const finish = (accepted) => {
@@ -108527,7 +109407,7 @@ This grants an additional recurring budget. Each occurrence reprices within the
108527
109407
  rl.close();
108528
109408
  if (!accepted)
108529
109409
  process.exitCode = 130;
108530
- resolve22(accepted);
109410
+ resolve23(accepted);
108531
109411
  };
108532
109412
  const timer = setTimeout(() => finish(false), 5 * 60 * 1000);
108533
109413
  rl.once("SIGINT", () => finish(false));
@@ -108786,9 +109666,9 @@ var init_completion = __esm(() => {
108786
109666
  });
108787
109667
 
108788
109668
  // src/lib/portable-snapshot-filter.ts
108789
- import { readdirSync as readdirSync20, statSync as statSync18 } from "fs";
109669
+ import { readdirSync as readdirSync21, statSync as statSync18 } from "fs";
108790
109670
  import { homedir as homedir11 } from "os";
108791
- import { join as join55, sep as sep9 } from "path";
109671
+ import { join as join56, sep as sep10 } from "path";
108792
109672
  function isExcludedSkillFileName(fileName) {
108793
109673
  if (EXCLUDE_FILE_NAMES.has(fileName)) {
108794
109674
  return true;
@@ -108811,27 +109691,27 @@ function isPortableWithinSkill(relativeParts) {
108811
109691
  function homePathFor(definition, homesRoot) {
108812
109692
  const home = homesRoot ?? homedir11();
108813
109693
  if (definition.subClass === "skills" || definition.subClass === "custom") {
108814
- return join55(skillsDataRootForHome(home), definition.name);
109694
+ return join56(skillsDataRootForHome(home), definition.name);
108815
109695
  }
108816
109696
  if (definition.agent === "opencode") {
108817
- return join55(home, ".config", "opencode", "skills");
109697
+ return join56(home, ".config", "opencode", "skills");
108818
109698
  }
108819
- return join55(home, `.${definition.agent}`, "skills");
109699
+ return join56(home, `.${definition.agent}`, "skills");
108820
109700
  }
108821
109701
  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));
109702
+ const category = definition.subClass === "agent-homes" ? join56("agent-homes", definition.agent ?? "") : definition.name;
109703
+ return join56("resources", stationId, "skills", category, ...relativePath.split(sep10));
108824
109704
  }
108825
109705
  function walkEntries(absoluteRoot) {
108826
109706
  let entries;
108827
109707
  try {
108828
- entries = readdirSync20(absoluteRoot, { withFileTypes: true });
109708
+ entries = readdirSync21(absoluteRoot, { withFileTypes: true });
108829
109709
  } catch {
108830
109710
  return [];
108831
109711
  }
108832
109712
  const output2 = [];
108833
109713
  for (const entry of entries) {
108834
- const childFull = join55(absoluteRoot, entry.name);
109714
+ const childFull = join56(absoluteRoot, entry.name);
108835
109715
  if (entry.isSymbolicLink()) {
108836
109716
  output2.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
108837
109717
  continue;
@@ -108842,7 +109722,7 @@ function walkEntries(absoluteRoot) {
108842
109722
  }
108843
109723
  const nested = walkEntries(childFull);
108844
109724
  for (const item of nested) {
108845
- output2.push({ ...item, relativePath: join55(entry.name, item.relativePath) });
109725
+ output2.push({ ...item, relativePath: join56(entry.name, item.relativePath) });
108846
109726
  }
108847
109727
  continue;
108848
109728
  }
@@ -108935,22 +109815,22 @@ var init_portable_snapshot_filter = __esm(() => {
108935
109815
  });
108936
109816
 
108937
109817
  // src/lib/station-snapshot.ts
108938
- import { createHash as createHash27 } from "crypto";
109818
+ import { createHash as createHash28 } from "crypto";
108939
109819
  import {
108940
- copyFileSync,
108941
- mkdirSync as mkdirSync25,
108942
- readFileSync as readFileSync38,
109820
+ copyFileSync as copyFileSync2,
109821
+ mkdirSync as mkdirSync26,
109822
+ readFileSync as readFileSync39,
108943
109823
  statSync as statSync19,
108944
- writeFileSync as writeFileSync24
109824
+ writeFileSync as writeFileSync25
108945
109825
  } from "fs";
108946
- import { dirname as dirname30, isAbsolute as isAbsolute23, relative as relative8, resolve as resolve22, sep as sep10 } from "path";
109826
+ import { dirname as dirname31, isAbsolute as isAbsolute24, relative as relative9, resolve as resolve23, sep as sep11 } from "path";
108947
109827
  function validateStationId(stationId) {
108948
109828
  if (!/^[a-z0-9-]+$/.test(stationId)) {
108949
109829
  throw new StationSnapshotError("INVALID_STATION", `station id must be a slug, got: ${stationId}`);
108950
109830
  }
108951
109831
  }
108952
109832
  function sha256File(filePath) {
108953
- return createHash27("sha256").update(readFileSync38(filePath)).digest("hex");
109833
+ return createHash28("sha256").update(readFileSync39(filePath)).digest("hex");
108954
109834
  }
108955
109835
  function scanHome(definition, homesRoot) {
108956
109836
  const homePath = homePathFor(definition, homesRoot);
@@ -108958,7 +109838,7 @@ function scanHome(definition, homesRoot) {
108958
109838
  const portable = [];
108959
109839
  const skipped = [];
108960
109840
  for (const entry of entries) {
108961
- const relativeParts = entry.relativePath.split(sep10);
109841
+ const relativeParts = entry.relativePath.split(sep11);
108962
109842
  const fileName = relativeParts[relativeParts.length - 1];
108963
109843
  if (entry.kind === "symlink") {
108964
109844
  skipped.push({ relativePath: entry.relativePath, reason: "symlink" });
@@ -109021,7 +109901,7 @@ function humanHomes(scanned) {
109021
109901
  }));
109022
109902
  }
109023
109903
  function writeStationSnapshot(options) {
109024
- const repoRoot = resolve22(options.repoRoot ?? process.cwd());
109904
+ const repoRoot = resolve23(options.repoRoot ?? process.cwd());
109025
109905
  const { scanned, plans, totalBytes } = planStationSnapshot(options);
109026
109906
  const manifestFiles = plans.map((plan) => ({
109027
109907
  relativePath: plan.source.relativePath,
@@ -109047,9 +109927,9 @@ function writeStationSnapshot(options) {
109047
109927
  const conflicts = [];
109048
109928
  const untouched = [];
109049
109929
  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)) {
109930
+ const destination = resolve23(repoRoot, plan.destination);
109931
+ const destinationRelative = relative9(repoRoot, destination);
109932
+ if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep11) || isAbsolute24(destinationRelative)) {
109053
109933
  throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
109054
109934
  }
109055
109935
  let existingDigest = null;
@@ -109070,9 +109950,9 @@ function writeStationSnapshot(options) {
109070
109950
  }
109071
109951
  let written = 0;
109072
109952
  for (const plan of untouched) {
109073
- const destination = resolve22(repoRoot, plan.destination);
109074
- mkdirSync25(dirname30(destination), { recursive: true });
109075
- copyFileSync(plan.source.fullPath, destination);
109953
+ const destination = resolve23(repoRoot, plan.destination);
109954
+ mkdirSync26(dirname31(destination), { recursive: true });
109955
+ copyFileSync2(plan.source.fullPath, destination);
109076
109956
  written += 1;
109077
109957
  }
109078
109958
  const unchanged3 = plans.length - untouched.length;
@@ -109089,9 +109969,9 @@ function writeStationSnapshot(options) {
109089
109969
  },
109090
109970
  files: manifestFiles
109091
109971
  };
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)}
109972
+ const manifestPath = resolve23(repoRoot, "resources", options.stationId, "skills", "sync-manifest.json");
109973
+ mkdirSync26(dirname31(manifestPath), { recursive: true });
109974
+ writeFileSync25(manifestPath, `${JSON.stringify(manifest, null, 2)}
109095
109975
  `);
109096
109976
  return {
109097
109977
  ...base3,
@@ -109122,8 +110002,8 @@ var exports_create_sync_config = {};
109122
110002
  __export(exports_create_sync_config, {
109123
110003
  registerCreateSync: () => registerCreateSync
109124
110004
  });
109125
- import { existsSync as existsSync33 } from "fs";
109126
- import { join as join56 } from "path";
110005
+ import { existsSync as existsSync34 } from "fs";
110006
+ import { join as join57 } from "path";
109127
110007
  function registerCreateSync(parent) {
109128
110008
  const configCmd = parent.command("config").description("Manage skills configuration");
109129
110009
  configCmd.command("show", { isDefault: true }).option("--json", "Output as JSON", false).description("Show current merged configuration").action((options) => {
@@ -109203,13 +110083,13 @@ function registerCreateSync(parent) {
109203
110083
  const pp = getConfigPath("project");
109204
110084
  if (options.json) {
109205
110085
  console.log(JSON.stringify({
109206
- global: { path: gp, exists: existsSync33(gp) },
109207
- project: { path: pp, exists: existsSync33(pp) }
110086
+ global: { path: gp, exists: existsSync34(gp) },
110087
+ project: { path: pp, exists: existsSync34(pp) }
109208
110088
  }, null, 2));
109209
110089
  return;
109210
110090
  }
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)")}`);
110091
+ console.log(`${source_default.cyan("global")}: ${gp}${existsSync34(gp) ? source_default.green(" (exists)") : source_default.dim(" (not found)")}`);
110092
+ console.log(`${source_default.cyan("project")}: ${pp}${existsSync34(pp) ? source_default.green(" (exists)") : source_default.dim(" (not found)")}`);
109213
110093
  });
109214
110094
  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
110095
  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 +110113,7 @@ function handleCreate(name, options) {
109233
110113
  console.log(source_default.green(`\u2713 Created custom skill '${result2.name}' at ${result2.path}`));
109234
110114
  console.log(source_default.dim(` Category: ${result2.manifest.category}`));
109235
110115
  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")}`);
110116
+ console.log(` ${source_default.cyan("Edit:")} ${join57(result2.path, options.kind === "instruction" ? "SKILL.md" : "src/index.ts")}`);
109237
110117
  console.log(` ${source_default.cyan("Prepare:")} skills prepare ${result2.name} --version <new-semver>`);
109238
110118
  }
109239
110119
  } catch (error61) {
@@ -109569,14 +110449,14 @@ var init_codex_native_config = () => {};
109569
110449
 
109570
110450
  // src/lib/codex-hook-rpc.ts
109571
110451
  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";
110452
+ import { realpathSync as realpathSync13, statSync as statSync20 } from "fs";
110453
+ import { isAbsolute as isAbsolute25 } from "path";
109574
110454
  import { promisify } from "util";
109575
110455
  async function connectCodexHookRpc(options) {
109576
- const found = isAbsolute24(options.command) ? options.command : Bun.which(options.command, { PATH: process.env.PATH });
110456
+ const found = isAbsolute25(options.command) ? options.command : Bun.which(options.command, { PATH: process.env.PATH });
109577
110457
  if (!found)
109578
110458
  throw new Error("CODEX_HOOK_TRUST_NATIVE_UNAVAILABLE: install Codex or pass --codex-command");
109579
- const binary = realpathSync12(found), stat3 = statSync20(binary);
110459
+ const binary = realpathSync13(found), stat3 = statSync20(binary);
109580
110460
  if (!stat3.isFile() || (stat3.mode & 18) !== 0)
109581
110461
  throw new Error("CODEX_HOOK_TRUST_UNSAFE_EXECUTABLE");
109582
110462
  let version2;
@@ -109589,7 +110469,7 @@ async function connectCodexHookRpc(options) {
109589
110469
  if (!SUPPORTED_CODEX_HOOK_VERSIONS.includes(version2))
109590
110470
  throw new Error("CODEX_HOOK_TRUST_NATIVE_UNSUPPORTED_VERSION");
109591
110471
  const current = statSync20(binary);
109592
- if (realpathSync12(found) !== binary || ["dev", "ino", "size", "mtimeMs", "ctimeMs"].some((k) => stat3[k] !== current[k]))
110472
+ if (realpathSync13(found) !== binary || ["dev", "ino", "size", "mtimeMs", "ctimeMs"].some((k) => stat3[k] !== current[k]))
109593
110473
  throw new Error("CODEX_HOOK_TRUST_NATIVE_EXECUTABLE_CHANGED");
109594
110474
  const env3 = { ...process.env, HOME: options.home, NO_COLOR: "1" };
109595
110475
  if (options.codexHome === undefined)
@@ -109664,13 +110544,13 @@ async function connectCodexHookRpc(options) {
109664
110544
  request(method2, params) {
109665
110545
  if (stopped)
109666
110546
  return Promise.reject(new Error("CODEX_HOOK_TRUST_NATIVE_RPC_CLOSED"));
109667
- return new Promise((resolve23, reject2) => {
110547
+ return new Promise((resolve24, reject2) => {
109668
110548
  const id2 = nextId++;
109669
110549
  const timer = setTimeout(() => {
109670
110550
  fail5();
109671
110551
  child.kill();
109672
110552
  }, 20000);
109673
- pending.set(id2, { resolve: resolve23, reject: reject2, timer });
110553
+ pending.set(id2, { resolve: resolve24, reject: reject2, timer });
109674
110554
  child.stdin.write(JSON.stringify({ id: id2, method: method2, params }) + `
109675
110555
  `);
109676
110556
  });
@@ -109680,14 +110560,14 @@ async function connectCodexHookRpc(options) {
109680
110560
  child.stdin.end();
109681
110561
  if (child.exitCode !== null || child.signalCode !== null)
109682
110562
  return;
109683
- await new Promise((resolve23) => {
110563
+ await new Promise((resolve24) => {
109684
110564
  const timer = setTimeout(() => {
109685
110565
  child.kill();
109686
- resolve23();
110566
+ resolve24();
109687
110567
  }, 1000);
109688
110568
  child.once("exit", () => {
109689
110569
  clearTimeout(timer);
109690
- resolve23();
110570
+ resolve24();
109691
110571
  });
109692
110572
  });
109693
110573
  }
@@ -109708,21 +110588,21 @@ var init_codex_hook_rpc = __esm(() => {
109708
110588
  });
109709
110589
 
109710
110590
  // 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";
110591
+ import { realpathSync as realpathSync14 } from "fs";
110592
+ import { dirname as dirname32, isAbsolute as isAbsolute26, join as join58 } from "path";
109713
110593
  import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
109714
110594
  function inspectRecordedSkillsCli(command, expected) {
109715
- need4(isAbsolute25(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
109716
- const resolved = realpathSync13(command);
110595
+ need4(isAbsolute26(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
110596
+ const resolved = realpathSync14(command);
109717
110597
  const cli = snapshot(resolved, false, { readOnlyPackage: true });
109718
- const manifest = snapshot(join57(dirname31(dirname31(resolved)), "package.json"), false, { readOnlyPackage: true });
110598
+ const manifest = snapshot(join58(dirname32(dirname32(resolved)), "package.json"), false, { readOnlyPackage: true });
109719
110599
  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");
110600
+ 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
110601
  need4((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
109722
110602
  const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
109723
110603
  need4(isDeepStrictEqual11(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
109724
110604
  return { receipt, recheck() {
109725
- need4(realpathSync13(command) === resolved, "SKILLS_COMMAND_CHANGED");
110605
+ need4(realpathSync14(command) === resolved, "SKILLS_COMMAND_CHANGED");
109726
110606
  unchanged2(cli);
109727
110607
  unchanged2(manifest);
109728
110608
  } };
@@ -109730,22 +110610,22 @@ function inspectRecordedSkillsCli(command, expected) {
109730
110610
  function bindSkillsCli(command, reviewed) {
109731
110611
  const normal = Bun.which("skills", { PATH: process.env.PATH });
109732
110612
  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");
110613
+ const resolved = realpathSync14(normal);
110614
+ const bound = isAbsolute26(command) ? command : command === "skills" ? normal : undefined;
110615
+ need4(bound && realpathSync14(bound) === resolved, "SKILLS_COMMAND_MISMATCH");
109736
110616
  const entrypoint = reviewed?.path ?? process.argv[1];
109737
- need4(entrypoint && realpathSync13(entrypoint) === resolved, "SKILLS_ENTRYPOINT_MISMATCH");
110617
+ need4(entrypoint && realpathSync14(entrypoint) === resolved, "SKILLS_ENTRYPOINT_MISMATCH");
109738
110618
  const cli = snapshot(resolved, false, { readOnlyPackage: true });
109739
- const manifest = snapshot(join57(dirname31(dirname31(resolved)), "package.json"), false, { readOnlyPackage: true });
110619
+ const manifest = snapshot(join58(dirname32(dirname32(resolved)), "package.json"), false, { readOnlyPackage: true });
109740
110620
  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");
110621
+ 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
110622
  if (reviewed)
109743
110623
  need4(/^[a-f0-9]{64}$/.test(reviewed.sha256) && reviewed.sha256 === cli.sha256 && reviewed.version === pkg.version, "SKILLS_RELEASE_MISMATCH");
109744
110624
  need4((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
109745
110625
  return {
109746
110626
  receipt: { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 },
109747
110627
  recheck() {
109748
- need4(Bun.which("skills", { PATH: process.env.PATH }) === normal && realpathSync13(normal) === resolved && realpathSync13(bound) === resolved && realpathSync13(entrypoint) === resolved, "SKILLS_COMMAND_CHANGED");
110628
+ need4(Bun.which("skills", { PATH: process.env.PATH }) === normal && realpathSync14(normal) === resolved && realpathSync14(bound) === resolved && realpathSync14(entrypoint) === resolved, "SKILLS_COMMAND_CHANGED");
109749
110629
  unchanged2(cli);
109750
110630
  unchanged2(manifest);
109751
110631
  }
@@ -109779,23 +110659,23 @@ var init_codex_hook_policy_recovery = __esm(() => {
109779
110659
  });
109780
110660
 
109781
110661
  // 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";
110662
+ import { lstatSync as lstatSync30, mkdirSync as mkdirSync27, readdirSync as readdirSync22, readFileSync as readFileSync40, realpathSync as realpathSync15, statSync as statSync21 } from "fs";
110663
+ import { createHash as createHash29, randomUUID as randomUUID11 } from "crypto";
109784
110664
  import { execFileSync } from "child_process";
109785
- import { dirname as dirname32, isAbsolute as isAbsolute26, join as join58, resolve as resolve23 } from "path";
110665
+ import { dirname as dirname33, isAbsolute as isAbsolute27, join as join59, resolve as resolve24 } from "path";
109786
110666
  import { homedir as homedir12 } from "os";
109787
110667
  import { isDeepStrictEqual as isDeepStrictEqual13 } from "util";
109788
110668
  function verifyReleaseArtifact(proof, skillsCli) {
109789
- need4(isAbsolute26(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
109790
- const tarPath = realpathSync14(proof.packageTarPath);
110669
+ need4(isAbsolute27(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
110670
+ const tarPath = realpathSync15(proof.packageTarPath);
109791
110671
  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)]];
110672
+ const tarBytes = readFileSync40(tarPath);
110673
+ need4(tarBytes.byteLength === proof.packageTarBytes && createHash29("sha256").update(tarBytes).digest("hex") === proof.packageTarSha256, "RECONCILE_RELEASE_ARTIFACT_CHANGED");
110674
+ const packageRoot = dirname33(dirname33(realpathSync15(skillsCli.receipt.path)));
110675
+ const manifest = readFileSync40(join59(packageRoot, "package.json"));
110676
+ need4(createHash29("sha256").update(manifest).digest("hex") === skillsCli.receipt.manifestSha256 && skillsCli.receipt.manifestSha256 === proof.manifestSha256, "RECONCILE_RELEASE_MANIFEST_CHANGED");
110677
+ need4(createHash29("sha256").update(readFileSync40(skillsCli.receipt.path)).digest("hex") === proof.executableSha256 && skillsCli.receipt.sha256 === proof.executableSha256, "RECONCILE_RELEASE_EXECUTABLE_CHANGED");
110678
+ const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync40(skillsCli.receipt.path)]];
109799
110679
  for (const [member, expected] of members2) {
109800
110680
  let actual;
109801
110681
  try {
@@ -109809,26 +110689,26 @@ function verifyReleaseArtifact(proof, skillsCli) {
109809
110689
  async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc) {
109810
110690
  let rpc;
109811
110691
  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);
110692
+ const home = resolve24(options.home ?? homedir12()), dataDir3 = resolve24(options.dataDir ?? getDataDirReadOnly());
110693
+ const parent = resolve24(join59(dataDir3, "native-hook-trust")), journal = resolve24(options.journal);
109814
110694
  need4(journal.startsWith(parent + "/") && /^[a-f0-9-]{36}$/.test(journal.slice(parent.length + 1)), "RECONCILE_JOURNAL_PATH");
109815
- const dir = lstatSync29(journal, { throwIfNoEntry: false });
110695
+ const dir = lstatSync30(journal, { throwIfNoEntry: false });
109816
110696
  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);
110697
+ need4(!lstatSync30(join59(journal, "receipt.json"), { throwIfNoEntry: false }), "RECONCILE_ALREADY_COMPLETE");
110698
+ need4(lstatSync30(join59(journal, "stopped.json"), { throwIfNoEntry: false })?.isFile(), "RECONCILE_JOURNAL_INCOMPLETE");
110699
+ const stoppedFile = snapshot(join59(journal, "stopped.json"), true);
110700
+ const intentFile = snapshot(join59(journal, "intent.json"), true), intent = JSON.parse(intentFile.text);
110701
+ 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
110702
  const legacyIntent = intent.home === undefined && intent.configSha256 === undefined && intent.configVersion === undefined && intent.declarations === undefined && intent.admitted === undefined && intent.nativeHooks === undefined;
109823
110703
  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 }));
110704
+ 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
110705
  const admitted = intent.admitted ?? intent.hooks;
109826
110706
  const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
109827
110707
  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
110708
  need4(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
109829
110709
  need4(isDeepStrictEqual13(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
109830
110710
  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);
110711
+ const policyCurrent = snapshot(join59(dataDir3, "agent-policy.json"), true);
109832
110712
  const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
109833
110713
  const recovery = claudeRecovery ?? additionsRecovery;
109834
110714
  need4(!recovery || !options.supersedeBinding, "RECONCILE_RECOVERY_MODE_CONFLICT");
@@ -109845,17 +110725,17 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109845
110725
  if (supersededBinding || recovery) {
109846
110726
  need4(Array.isArray(intent.nativeHooks) && intent.nativeHooks.length > 0, "RECONCILE_NATIVE_INVENTORY_REQUIRED");
109847
110727
  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");
110728
+ 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
110729
  }
109850
110730
  const currentConfig = snapshot(intent.configPath, true);
109851
110731
  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");
110732
+ const alias = binding.rootAliases?.find((item) => item.agent === "codex"), physicalRoot = alias?.target ?? join59(home, ".codex"), hooksPath = join59(physicalRoot, "hooks.json");
110733
+ need4(resolve24(intent.configPath) === resolve24(join59(physicalRoot, "config.toml")), "RECONCILE_CONFIG_PATH_CHANGED");
109854
110734
  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);
110735
+ need4([join59(home, ".codex/config.toml"), join59(physicalRoot, "config.toml")].includes(nativeConfigPath), "RECONCILE_NATIVE_CONFIG_PATH_CHANGED");
110736
+ const codexHome = nativeConfigPath !== intent.configPath ? undefined : dirname33(nativeConfigPath);
109857
110737
  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;
110738
+ 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
110739
  const managedHooks = [...intent.hooks, ...additions?.hooks ?? []];
109860
110740
  const managedKeys = managedHooks.map((h) => h.key);
109861
110741
  const witnessSame = codexTrustReconcileWitness(configBefore.text, managedKeys) === codexTrustReconcileWitness(currentConfig.text, managedKeys);
@@ -109919,7 +110799,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109919
110799
  need4(isDeepStrictEqual13(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
109920
110800
  }
109921
110801
  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));
110802
+ const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join59(home, ".codex/hooks.json"), hooksPath));
109923
110803
  need4(matches2.length === 1, "RECONCILE_NATIVE_IDENTITY_CHANGED");
109924
110804
  const h = matches2[0];
109925
110805
  const trusted = h.trustStatus === "trusted";
@@ -109968,7 +110848,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109968
110848
  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
110849
  if (preview2)
109970
110850
  return receipt;
109971
- save(join58(journal, "receipt.json"), json2(receipt));
110851
+ save(join59(journal, "receipt.json"), json2(receipt));
109972
110852
  return receipt;
109973
110853
  } catch (error61) {
109974
110854
  const message = error61 instanceof Error && error61.message.startsWith("CODEX_HOOK_TRUST_") ? error61.message : "CODEX_HOOK_TRUST_RECONCILE_REFUSED";
@@ -109980,15 +110860,15 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109980
110860
  async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRpc) {
109981
110861
  let rpc, journal;
109982
110862
  try {
109983
- const home = resolve23(options.home ?? homedir12()), dataDir3 = resolve23(options.dataDir ?? getDataDirReadOnly());
110863
+ const home = resolve24(options.home ?? homedir12()), dataDir3 = resolve24(options.dataDir ?? getDataDirReadOnly());
109984
110864
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home });
109985
- const policyFile = snapshot(join58(dataDir3, "agent-policy.json"), true), policy = parseManagedSkillPolicy(policyFile.text);
110865
+ const policyFile = snapshot(join59(dataDir3, "agent-policy.json"), true), policy = parseManagedSkillPolicy(policyFile.text);
109986
110866
  const binding = policy.bridge, alias = binding.rootAliases?.find((item) => item.agent === "codex");
109987
110867
  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");
110868
+ const physicalRoot = alias?.target ?? join59(home, ".codex"), hooksPath = join59(physicalRoot, "hooks.json"), configPath = join59(physicalRoot, "config.toml");
110869
+ need4(options.codexHome === undefined || isAbsolute27(options.codexHome) && [join59(home, ".codex"), physicalRoot].includes(options.codexHome), "NATIVE_HOME_NOT_ADMITTED");
109990
110870
  const codexHome = options.codexHome === undefined ? undefined : physicalRoot;
109991
- const nativeConfigPath = join58(codexHome ?? join58(home, ".codex"), "config.toml");
110871
+ const nativeConfigPath = join59(codexHome ?? join59(home, ".codex"), "config.toml");
109992
110872
  const hooksFile = snapshot(hooksPath), configFile = snapshot(configPath, true), original = structure3(configFile.text);
109993
110873
  const declarations = JSON.parse(hooksFile.text).hooks, expected = [];
109994
110874
  for (const [event, [nativeEvent, label]] of Object.entries(eventNames)) {
@@ -109997,25 +110877,25 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
109997
110877
  need4(Array.isArray(groups), "DECLARATION_CHANGED");
109998
110878
  const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
109999
110879
  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 });
110880
+ expected.push({ event: nativeEvent, key: `${join59(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
110001
110881
  }
110002
110882
  codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
110003
110883
  rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
110004
110884
  need4(SUPPORTED_CODEX_HOOK_VERSIONS.includes(rpc.version), "NATIVE_UNSUPPORTED_VERSION");
110005
110885
  const list2 = async () => {
110006
110886
  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`));
110887
+ const nativeRoots = [...new Set([physicalRoot, join59(home, ".codex")])];
110888
+ 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
110889
  const entry = response?.data?.[0];
110010
110890
  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
110891
  return response.data[0].hooks;
110012
110892
  };
110013
110893
  const admit = (hooks) => expected.map((wanted) => {
110014
- const canonicalKey = wanted.key.replace(join58(home, ".codex/hooks.json"), hooksPath);
110894
+ const canonicalKey = wanted.key.replace(join59(home, ".codex/hooks.json"), hooksPath);
110015
110895
  const matches2 = hooks.filter((h2) => h2.key === wanted.key || h2.key === canonicalKey);
110016
110896
  need4(matches2.length === 1, "AMBIGUOUS_IDENTITY");
110017
110897
  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");
110898
+ 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
110899
  need4(hooks.filter((other) => other.command === wanted.command).length === 1, "AMBIGUOUS_IDENTITY");
110020
110900
  need4(["trusted", "untrusted", "modified"].includes(h.trustStatus), "UNKNOWN_TRUST_STATUS");
110021
110901
  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 +110921,27 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
110041
110921
  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
110922
  if (options.apply)
110043
110923
  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 });
110924
+ const parent = join59(dataDir3, "native-hook-trust");
110925
+ const st = lstatSync30(parent, { throwIfNoEntry: false });
110046
110926
  if (st) {
110047
110927
  need4(st.isDirectory() && !st.isSymbolicLink() && st.uid === process.getuid() && (st.mode & 511) === 448, "UNSAFE_JOURNAL");
110048
- const priorJournals = readdirSync21(parent);
110928
+ const priorJournals = readdirSync22(parent);
110049
110929
  need4(priorJournals.length < 1000, "JOURNAL_BOUND");
110050
110930
  for (const name of priorJournals) {
110051
110931
  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");
110932
+ need4(lstatSync30(join59(parent, name, "receipt.json"), { throwIfNoEntry: false })?.isFile(), "RECONCILE_REQUIRED: inspect the incomplete private journal before applying again");
110053
110933
  }
110054
110934
  }
110055
110935
  if (!options.apply || !planned.length)
110056
110936
  return baseReceipt;
110057
110937
  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) }));
110938
+ mkdirSync27(parent, { mode: 448 });
110939
+ journal = join59(parent, randomUUID11());
110940
+ mkdirSync27(journal, { mode: 448 });
110941
+ save(join59(journal, "config.before.toml"), configFile.bytes);
110942
+ save(join59(journal, "hooks.before.json"), hooksFile.bytes);
110943
+ save(join59(journal, "policy.before.json"), policyFile.bytes);
110944
+ save(join59(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
110065
110945
  need4(isDeepStrictEqual13(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
110066
110946
  checkInputs();
110067
110947
  let result2;
@@ -110077,7 +110957,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
110077
110957
  for (const h of planned)
110078
110958
  expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
110079
110959
  need4(isDeepStrictEqual13(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
110080
- const savedConfig = snapshot(join58(journal, "config.before.toml"), true);
110960
+ const savedConfig = snapshot(join59(journal, "config.before.toml"), true);
110081
110961
  need4(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
110082
110962
  const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
110083
110963
  unchanged2(policyFile);
@@ -110093,13 +110973,13 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
110093
110973
  skillsCli.recheck();
110094
110974
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
110095
110975
  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));
110976
+ save(join59(journal, "receipt.json"), json2(receipt));
110097
110977
  return receipt;
110098
110978
  } catch (error61) {
110099
110979
  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
110980
  if (journal) {
110101
110981
  try {
110102
- save(join58(journal, "stopped.json"), json2({ error: message.split(":")[0], automaticRollback: false, reconcileBeforeRetry: true }));
110982
+ save(join59(journal, "stopped.json"), json2({ error: message.split(":")[0], automaticRollback: false, reconcileBeforeRetry: true }));
110103
110983
  } catch {}
110104
110984
  }
110105
110985
  throw new Error(message);
@@ -110108,7 +110988,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
110108
110988
  }
110109
110989
  }
110110
110990
  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) => {
110991
+ `, 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
110992
  const copy = structuredClone(value);
110113
110993
  if (copy?.bridge?.commands && Object.hasOwn(copy.bridge.commands, "codex"))
110114
110994
  copy.bridge.commands.codex = "<reviewed-codex-binding>";
@@ -110219,8 +111099,8 @@ var exports_agent_integration = {};
110219
111099
  __export(exports_agent_integration, {
110220
111100
  registerAgentIntegration: () => registerAgentIntegration
110221
111101
  });
110222
- import { readFileSync as readFileSync40 } from "fs";
110223
- import { isAbsolute as isAbsolute27 } from "path";
111102
+ import { readFileSync as readFileSync41 } from "fs";
111103
+ import { isAbsolute as isAbsolute28 } from "path";
110224
111104
  function pinnedHookProfile(input3, configuredProfile) {
110225
111105
  try {
110226
111106
  const parsed = parseSkillContextInput(JSON.stringify(input3));
@@ -110297,7 +111177,7 @@ function registerAgentIntegration(parent) {
110297
111177
  });
110298
111178
  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
111179
  try {
110300
- const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync40(options.discoveryInputs, "utf8")) : undefined;
111180
+ const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync41(options.discoveryInputs, "utf8")) : undefined;
110301
111181
  const plan = planAgentIntegration({ agents: agents(options.agent), command: options.command, profileId: options.selectionProfile, includeVendor: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases });
110302
111182
  const result2 = options.apply ? applyAgentIntegration(plan) : { changed: [], backups: [] };
110303
111183
  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 +111235,7 @@ function registerAgentIntegration(parent) {
110355
111235
  try {
110356
111236
  if (agents(options.agent).length !== 1)
110357
111237
  throw new Error("A hook invocation requires one agent");
110358
- const inputText = readFileSync40(0, "utf8");
111238
+ const inputText = readFileSync41(0, "utf8");
110359
111239
  if (inputText.length > 1024 * 1024)
110360
111240
  throw new Error("Hook input is too large");
110361
111241
  let input3 = JSON.parse(inputText);
@@ -110368,12 +111248,12 @@ function registerAgentIntegration(parent) {
110368
111248
  input3.hook_event_name = event;
110369
111249
  const projects = [process.cwd()];
110370
111250
  if (input3.cwd !== undefined) {
110371
- if (typeof input3.cwd !== "string" || !isAbsolute27(input3.cwd) || input3.cwd.includes("\x00"))
111251
+ if (typeof input3.cwd !== "string" || !isAbsolute28(input3.cwd) || input3.cwd.includes("\x00"))
110372
111252
  throw new Error("Invalid native hook working directory");
110373
111253
  projects.push(input3.cwd);
110374
111254
  }
110375
111255
  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")))
111256
+ 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
111257
  throw new Error("Invalid Cursor workspace roots");
110378
111258
  projects.push(...input3.workspace_roots);
110379
111259
  input3.cwd ??= input3.workspace_roots[0] ?? process.cwd();
@@ -110453,7 +111333,7 @@ function registerAgentIntegration(parent) {
110453
111333
  try {
110454
111334
  if (options.agent && !INTEGRATION_AGENTS.includes(options.agent))
110455
111335
  throw new Error(`Unsupported migration agent: ${options.agent}`);
110456
- const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync40(options.discoveryInputs, "utf8")) : undefined;
111336
+ const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync41(options.discoveryInputs, "utf8")) : undefined;
110457
111337
  if (options.targetManifest && (options.includeUnmanaged || options.includeVendor))
110458
111338
  throw new Error("--target-manifest cannot be combined with broad native migration selectors");
110459
111339
  const targetManifest = options.targetManifest ? readNativeMigrationTargetManifest(options.targetManifest) : undefined;
@@ -110623,7 +111503,7 @@ var exports_profiles = {};
110623
111503
  __export(exports_profiles, {
110624
111504
  registerProfiles: () => registerProfiles
110625
111505
  });
110626
- import { closeSync as closeSync23, constants as constants20, fstatSync as fstatSync23, openSync as openSync23, readSync as readSync17, writeFileSync as writeFileSync25 } from "fs";
111506
+ import { closeSync as closeSync23, constants as constants20, fstatSync as fstatSync23, openSync as openSync23, readSync as readSync17, writeFileSync as writeFileSync26 } from "fs";
110627
111507
  function readProfileInput(path) {
110628
111508
  const fd = openSync23(path, constants20.O_RDONLY | constants20.O_NONBLOCK);
110629
111509
  try {
@@ -110651,7 +111531,7 @@ function registerProfiles(parent) {
110651
111531
  try {
110652
111532
  const profile = await readSkillProfile(id3), text4 = JSON.stringify(profile, null, 2);
110653
111533
  if (options.save)
110654
- writeFileSync25(options.save, JSON.stringify(profile), { mode: 384, flag: "wx" });
111534
+ writeFileSync26(options.save, JSON.stringify(profile), { mode: 384, flag: "wx" });
110655
111535
  await writeCliOutput(options.json ? text4 : `${profile.id} at ${profile.revision}: ${profile.selections.map((skill) => `${skill.slug}@${skill.version}`).join(", ")}`);
110656
111536
  } catch (error61) {
110657
111537
  console.error(error61.message);
@@ -110698,7 +111578,7 @@ import {
110698
111578
  fstatSync as fstatSync24,
110699
111579
  openSync as openSync24,
110700
111580
  readSync as readSync18,
110701
- writeFileSync as writeFileSync26
111581
+ writeFileSync as writeFileSync27
110702
111582
  } from "fs";
110703
111583
  function readInput(path) {
110704
111584
  const fd = openSync24(path, constants21.O_RDONLY | constants21.O_NONBLOCK);
@@ -110731,7 +111611,7 @@ function registerGrants(parent) {
110731
111611
  try {
110732
111612
  const policy = await readExecutionGrantPolicy(profile, options.revision);
110733
111613
  if (options.save)
110734
- writeFileSync26(options.save, JSON.stringify(policy, null, 2), {
111614
+ writeFileSync27(options.save, JSON.stringify(policy, null, 2), {
110735
111615
  mode: 384,
110736
111616
  flag: "wx"
110737
111617
  });
@@ -110758,28 +111638,28 @@ var init_grants = __esm(() => {
110758
111638
  });
110759
111639
 
110760
111640
  // src/lib/station-hydrate.ts
110761
- import { createHash as createHash29 } from "crypto";
111641
+ import { createHash as createHash30 } from "crypto";
110762
111642
  import {
110763
- copyFileSync as copyFileSync2,
110764
- mkdirSync as mkdirSync27,
110765
- readdirSync as readdirSync22,
110766
- readFileSync as readFileSync41,
111643
+ copyFileSync as copyFileSync3,
111644
+ mkdirSync as mkdirSync28,
111645
+ readdirSync as readdirSync23,
111646
+ readFileSync as readFileSync42,
110767
111647
  statSync as statSync22,
110768
- writeFileSync as writeFileSync27
111648
+ writeFileSync as writeFileSync28
110769
111649
  } from "fs";
110770
- import { dirname as dirname33, join as join59, resolve as resolve24, sep as sep11 } from "path";
111650
+ import { dirname as dirname34, join as join60, resolve as resolve25, sep as sep12 } from "path";
110771
111651
  function fail5(code2, message, detail = []) {
110772
111652
  throw new StationSnapshotError(code2, message, detail);
110773
111653
  }
110774
111654
  function snapshotRootFor(repoRoot, stationId) {
110775
- return join59(repoRoot, "resources", stationId, "skills");
111655
+ return join60(repoRoot, "resources", stationId, "skills");
110776
111656
  }
110777
111657
  function readSnapshotManifest(repoRoot, stationId) {
110778
111658
  const snapshotRoot = snapshotRootFor(repoRoot, stationId);
110779
- const manifestPath = join59(snapshotRoot, "sync-manifest.json");
111659
+ const manifestPath = join60(snapshotRoot, "sync-manifest.json");
110780
111660
  let manifest;
110781
111661
  try {
110782
- manifest = JSON.parse(readFileSync41(manifestPath, "utf8"));
111662
+ manifest = JSON.parse(readFileSync42(manifestPath, "utf8"));
110783
111663
  } catch (error61) {
110784
111664
  fail5("MANIFEST_UNREADABLE", `cannot read snapshot manifest: ${manifestPath}: ${error61.message}`);
110785
111665
  }
@@ -110803,10 +111683,10 @@ function planStationHydration(stationId, repoRoot) {
110803
111683
  const hashMismatches = [];
110804
111684
  const skippedByRule = [];
110805
111685
  for (const agent of SYNC_AGENTS) {
110806
- const agentRoot = join59(snapshotRoot, "agent-homes", agent);
111686
+ const agentRoot = join60(snapshotRoot, "agent-homes", agent);
110807
111687
  let identEntries;
110808
111688
  try {
110809
- identEntries = readdirSync22(agentRoot, { withFileTypes: true });
111689
+ identEntries = readdirSync23(agentRoot, { withFileTypes: true });
110810
111690
  } catch {
110811
111691
  continue;
110812
111692
  }
@@ -110814,10 +111694,10 @@ function planStationHydration(stationId, repoRoot) {
110814
111694
  if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
110815
111695
  continue;
110816
111696
  }
110817
- const identRoot = join59(agentRoot, identEntry.name);
111697
+ const identRoot = join60(agentRoot, identEntry.name);
110818
111698
  const entries = walkEntries(identRoot);
110819
111699
  for (const entry of entries) {
110820
- const relativeParts = [identEntry.name, ...entry.relativePath.split(sep11)];
111700
+ const relativeParts = [identEntry.name, ...entry.relativePath.split(sep12)];
110821
111701
  if (entry.kind === "symlink") {
110822
111702
  symlinks.push({ ident: identEntry.name, agent, relativePath: entry.relativePath });
110823
111703
  continue;
@@ -110841,8 +111721,8 @@ function planStationHydration(stationId, repoRoot) {
110841
111721
  });
110842
111722
  continue;
110843
111723
  }
110844
- const withinIdent = relativeParts.slice(1).join(sep11);
110845
- const homeRelative = relativeParts.join(sep11);
111724
+ const withinIdent = relativeParts.slice(1).join(sep12);
111725
+ const homeRelative = relativeParts.join(sep12);
110846
111726
  if (REFUSED_SCANNER_FLAGGED.has(homeRelative)) {
110847
111727
  skippedByRule.push({
110848
111728
  ident: identEntry.name,
@@ -110915,7 +111795,7 @@ function planStationHydration(stationId, repoRoot) {
110915
111795
  for (const copy of copies) {
110916
111796
  let isStub = false;
110917
111797
  try {
110918
- isStub = isPointerSkillMd(readFileSync41(copy.fullPath, "utf8"));
111798
+ isStub = isPointerSkillMd(readFileSync42(copy.fullPath, "utf8"));
110919
111799
  } catch {
110920
111800
  isStub = false;
110921
111801
  }
@@ -110961,12 +111841,12 @@ function skillSha256(skill) {
110961
111841
  return sha256File(skill.files[0].winner.fullPath);
110962
111842
  }
110963
111843
  const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
110964
- return createHash29("sha256").update(joined.sort().join(`
111844
+ return createHash30("sha256").update(joined.sort().join(`
110965
111845
  `)).digest("hex");
110966
111846
  }
110967
111847
  function writeStationHydration(options) {
110968
- const repoRoot = resolve24(options.repoRoot ?? process.cwd());
110969
- const cacheRoot = resolve24(options.cacheRoot ?? resolveCorpusRoot());
111848
+ const repoRoot = resolve25(options.repoRoot ?? process.cwd());
111849
+ const cacheRoot = resolve25(options.cacheRoot ?? resolveCorpusRoot());
110970
111850
  const plan = planStationHydration(options.stationId, repoRoot);
110971
111851
  const resultSkills = plan.winners.map((skill) => ({
110972
111852
  ident: skill.ident,
@@ -110999,7 +111879,7 @@ function writeStationHydration(options) {
110999
111879
  const toWrite = [];
111000
111880
  for (const skill of plan.winners) {
111001
111881
  for (const file2 of skill.files) {
111002
- const destination = join59(cacheRoot, skill.ident, file2.withinIdent);
111882
+ const destination = join60(cacheRoot, skill.ident, file2.withinIdent);
111003
111883
  const digest4 = sha256File(file2.winner.fullPath);
111004
111884
  let existingDigest = null;
111005
111885
  try {
@@ -111020,8 +111900,8 @@ function writeStationHydration(options) {
111020
111900
  }
111021
111901
  let written = 0;
111022
111902
  for (const entry of toWrite) {
111023
- mkdirSync27(dirname33(entry.destination), { recursive: true });
111024
- copyFileSync2(entry.fullPath, entry.destination);
111903
+ mkdirSync28(dirname34(entry.destination), { recursive: true });
111904
+ copyFileSync3(entry.fullPath, entry.destination);
111025
111905
  written += 1;
111026
111906
  }
111027
111907
  const unchanged3 = plan.totalFiles - written;
@@ -111041,9 +111921,9 @@ function writeStationHydration(options) {
111041
111921
  },
111042
111922
  skills: resultSkills
111043
111923
  };
111044
- const hydrationManifestPath = join59(dirname33(cacheRoot), `hydration-${options.stationId}.json`);
111045
- mkdirSync27(dirname33(hydrationManifestPath), { recursive: true });
111046
- writeFileSync27(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
111924
+ const hydrationManifestPath = join60(dirname34(cacheRoot), `hydration-${options.stationId}.json`);
111925
+ mkdirSync28(dirname34(hydrationManifestPath), { recursive: true });
111926
+ writeFileSync28(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
111047
111927
  `);
111048
111928
  return {
111049
111929
  ...base3,
@@ -111118,15 +111998,15 @@ var init_hydrate = __esm(() => {
111118
111998
  });
111119
111999
 
111120
112000
  // 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";
112001
+ import { createHash as createHash31, randomUUID as randomUUID12 } from "crypto";
112002
+ 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
112003
  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";
112004
+ import { dirname as dirname35, isAbsolute as isAbsolute29, join as join61, relative as relative10, resolve as resolve26, sep as sep13 } from "path";
111125
112005
  function prepareSkill(name, options) {
111126
112006
  const normalized = normalizePortableSkillName(name);
111127
112007
  if (!new RegExp(SEMVER_PATTERN).test(options.version))
111128
112008
  throw new Error("--version must be a semantic version, for example 0.2.0.");
111129
- const path = join60(getPortableSkillsRootReadOnly(options), normalized);
112009
+ const path = join61(getPortableSkillsRootReadOnly(options), normalized);
111130
112010
  assertPortableAuthoringPath(path, options);
111131
112011
  const before2 = snapshot2(path);
111132
112012
  const manifestFile = before2.files.find((file2) => file2.path === "skill.json");
@@ -111161,17 +112041,17 @@ function prepareSkill(name, options) {
111161
112041
  throw new Error("skill.json provenance must be an object.");
111162
112042
  const provenance = manifest.provenance;
111163
112043
  const candidate2 = { ...manifest, version: options.version, kind, provenance: { ...provenance } };
111164
- const staging = mkdtempSync6(join60(tmpdir2(), "skills-prepare-"));
112044
+ const staging = mkdtempSync6(join61(tmpdir2(), "skills-prepare-"));
111165
112045
  try {
111166
112046
  for (const directory2 of before2.directories)
111167
- mkdirSync28(join60(staging, directory2), { recursive: true });
112047
+ mkdirSync29(join61(staging, directory2), { recursive: true });
111168
112048
  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 });
112049
+ const destination = join61(staging, file2.path);
112050
+ mkdirSync29(dirname35(destination), { recursive: true });
112051
+ writeFileSync29(destination, file2.bytes, { mode: file2.mode });
111172
112052
  }
111173
- const stagedManifest = join60(staging, "skill.json");
111174
- writeFileSync28(stagedManifest, `${JSON.stringify(candidate2, null, 2)}
112053
+ const stagedManifest = join61(staging, "skill.json");
112054
+ writeFileSync29(stagedManifest, `${JSON.stringify(candidate2, null, 2)}
111175
112055
  `);
111176
112056
  const contentHash = computeContentHash(staging);
111177
112057
  const changed3 = options.version !== previousVersion || kind !== manifest.kind || contentHash !== provenance?.content_hash;
@@ -111181,7 +112061,7 @@ function prepareSkill(name, options) {
111181
112061
  candidate2.provenance.content_hash = contentHash;
111182
112062
  const candidateBytes = `${JSON.stringify(candidate2, null, 2)}
111183
112063
  `;
111184
- writeFileSync28(stagedManifest, candidateBytes);
112064
+ writeFileSync29(stagedManifest, candidateBytes);
111185
112065
  validateRawCandidate(candidate2);
111186
112066
  validateRuntimeEntrypoint(candidate2, staging);
111187
112067
  const validation = validatePortableSkillDirectory(normalized, staging);
@@ -111193,11 +112073,11 @@ ${validation.issues.map((issue2) => `${issue2.code}: ${issue2.message}`).join(`
111193
112073
  if (snapshot2(path).identity !== before2.identity)
111194
112074
  throw new Error("Skill changed during preparation; review the draft and run prepare again.");
111195
112075
  if (changed3 && !options.dryRun) {
111196
- const temporary = join60(path, `.skill-prepare-${randomUUID11()}.tmp`);
112076
+ const temporary = join61(path, `.skill-prepare-${randomUUID12()}.tmp`);
111197
112077
  try {
111198
- writeFileSync28(temporary, candidateBytes, { flag: "wx", mode: manifestFile.mode });
111199
- chmodSync4(temporary, manifestFile.mode);
111200
- renameSync13(temporary, join60(path, "skill.json"));
112078
+ writeFileSync29(temporary, candidateBytes, { flag: "wx", mode: manifestFile.mode });
112079
+ chmodSync5(temporary, manifestFile.mode);
112080
+ renameSync14(temporary, join61(path, "skill.json"));
111201
112081
  } finally {
111202
112082
  rmSync9(temporary, { force: true });
111203
112083
  }
@@ -111227,8 +112107,8 @@ function validateRuntimeEntrypoint(candidate2, root2) {
111227
112107
  const entry = candidate2.runtime.entrypoint;
111228
112108
  if (typeof entry !== "string")
111229
112109
  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()) {
112110
+ const target = resolve26(root2, entry), rel = relative10(root2, target);
112111
+ if (isAbsolute29(entry) || /^[a-zA-Z]:/.test(entry) || entry.includes("\\") || !rel || rel === ".." || rel.startsWith(`..${sep13}`) || isAbsolute29(rel) || !existsSync35(target) || !lstatSync31(target).isFile()) {
111232
112112
  throw new Error("Executable runtime.entrypoint must name an existing regular file inside the skill directory.");
111233
112113
  }
111234
112114
  }
@@ -111313,25 +112193,25 @@ function compareVersions(left, right) {
111313
112193
  return 0;
111314
112194
  }
111315
112195
  function snapshot2(root2) {
111316
- const rootStat = lstatSync30(root2);
112196
+ const rootStat = lstatSync31(root2);
111317
112197
  if (rootStat.isSymbolicLink() || !rootStat.isDirectory())
111318
112198
  throw new Error("Skill root must be a real directory, not a symlink.");
111319
112199
  const files = [], directories = [];
111320
- const hash5 = createHash30("sha256").update(`${rootStat.dev}:${rootStat.ino}\x00`);
112200
+ const hash6 = createHash31("sha256").update(`${rootStat.dev}:${rootStat.ino}\x00`);
111321
112201
  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()))
112202
+ const walk2 = (relative11) => {
112203
+ for (const name of readdirSync24(join61(root2, relative11)).sort()) {
112204
+ if (IGNORED.has(name.toLowerCase()) || !relative11 && ROOT_IGNORED.has(name.toLowerCase()))
111325
112205
  continue;
111326
- const path = relative10 ? `${relative10}/${name}` : name;
112206
+ const path = relative11 ? `${relative11}/${name}` : name;
111327
112207
  if (++count > 20000)
111328
112208
  throw new Error("Skill exceeds the preparation entry limit (20000).");
111329
- const absolute = join60(root2, path), stat3 = lstatSync30(absolute);
112209
+ const absolute = join61(root2, path), stat3 = lstatSync31(absolute);
111330
112210
  if (stat3.isSymbolicLink())
111331
112211
  throw new Error(`Skill contains a symlink: ${path}. No files were changed.`);
111332
112212
  if (stat3.isDirectory() && name === "node_modules")
111333
112213
  continue;
111334
- hash5.update(JSON.stringify([path, stat3.dev, stat3.ino, stat3.mode]));
112214
+ hash6.update(JSON.stringify([path, stat3.dev, stat3.ino, stat3.mode]));
111335
112215
  if (stat3.isDirectory()) {
111336
112216
  directories.push(path);
111337
112217
  walk2(path);
@@ -111339,18 +112219,18 @@ function snapshot2(root2) {
111339
112219
  bytes += stat3.size;
111340
112220
  if (bytes > 50000000)
111341
112221
  throw new Error("Skill exceeds the preparation size limit (50000000 bytes).");
111342
- const content = readFileSync42(absolute);
112222
+ const content = readFileSync43(absolute);
111343
112223
  if (content.length !== stat3.size)
111344
112224
  throw new Error("Skill changed during preparation.");
111345
112225
  files.push({ path, bytes: content, mode: stat3.mode & 511 });
111346
- hash5.update(`\x00${content.length}\x00`).update(content);
112226
+ hash6.update(`\x00${content.length}\x00`).update(content);
111347
112227
  } else
111348
112228
  throw new Error(`Unsupported skill entry: ${path}.`);
111349
- hash5.update("\x00");
112229
+ hash6.update("\x00");
111350
112230
  }
111351
112231
  };
111352
112232
  walk2("");
111353
- return { files, directories, identity: hash5.digest("hex") };
112233
+ return { files, directories, identity: hash6.digest("hex") };
111354
112234
  }
111355
112235
  var MAX_PACK_PARITY_PATHS = 8, MAX_PACK_PARITY_PATH_CHARS = 120, IGNORED, ROOT_IGNORED;
111356
112236
  var init_prepare_skill = __esm(() => {
@@ -111741,8 +112621,8 @@ var init_schedule = __esm(() => {
111741
112621
  });
111742
112622
 
111743
112623
  // 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";
112624
+ import { mkdirSync as mkdirSync30, writeFileSync as writeFileSync30 } from "fs";
112625
+ import { dirname as dirname36, relative as relative11 } from "path";
111746
112626
  function createRegistrySyncArtifact(options = {}) {
111747
112627
  const profile = options.profile ?? "all";
111748
112628
  const includeDocs = options.includeDocs ?? true;
@@ -111754,7 +112634,7 @@ function createRegistrySyncArtifact(options = {}) {
111754
112634
  const registry2 = [...loadRegistryProfile(profile)].sort((a, b) => a.name.localeCompare(b.name));
111755
112635
  const skills = registry2.map((skill) => {
111756
112636
  const skillPath = getSkillPath(skill.name);
111757
- const directory2 = relative10(process.cwd(), skillPath) || skillPath;
112637
+ const directory2 = relative11(process.cwd(), skillPath) || skillPath;
111758
112638
  const validation = includeValidation ? validateSkillDirectory(skill.name, skillPath, skill) : undefined;
111759
112639
  const docs = includeDocs ? buildDocs(skill.name) : undefined;
111760
112640
  return {
@@ -111799,8 +112679,8 @@ function createRegistrySyncArtifact(options = {}) {
111799
112679
  };
111800
112680
  }
111801
112681
  function writeRegistrySyncArtifact(path, artifact) {
111802
- mkdirSync29(dirname35(path), { recursive: true });
111803
- writeFileSync29(path, `${JSON.stringify(artifact, null, 2)}
112682
+ mkdirSync30(dirname36(path), { recursive: true });
112683
+ writeFileSync30(path, `${JSON.stringify(artifact, null, 2)}
111804
112684
  `);
111805
112685
  }
111806
112686
  function buildDocs(name) {
@@ -111819,8 +112699,8 @@ var init_registry_sync = __esm(() => {
111819
112699
  });
111820
112700
 
111821
112701
  // 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";
112702
+ 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";
112703
+ 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
112704
  function exactVersion(spec) {
111825
112705
  const at2 = spec.indexOf("@");
111826
112706
  const slug2 = spec.slice(0, at2);
@@ -111831,23 +112711,23 @@ function exactVersion(spec) {
111831
112711
  return { slug: slug2, version: version2 };
111832
112712
  }
111833
112713
  function assertPrivateDestination(output2) {
111834
- if (!isAbsolute29(output2) || resolve26(output2) !== output2 || !basename14(output2).endsWith(".tar.gz")) {
112714
+ if (!isAbsolute30(output2) || resolve27(output2) !== output2 || !basename15(output2).endsWith(".tar.gz")) {
111835
112715
  throw new Error("Review output must be an absolute, normalized .tar.gz path.");
111836
112716
  }
111837
- const parent = dirname36(output2);
111838
- if (realpathSync15(parent) !== parent || lstatSync31(parent).isSymbolicLink()) {
112717
+ const parent = dirname37(output2);
112718
+ if (realpathSync16(parent) !== parent || lstatSync32(parent).isSymbolicLink()) {
111839
112719
  throw new Error("Review output parent must be a real directory, not a symlink.");
111840
112720
  }
111841
112721
  const parentStat = statSync23(parent);
111842
112722
  if (!parentStat.isDirectory() || (parentStat.mode & 63) !== 0 || process.getuid && parentStat.uid !== process.getuid()) {
111843
112723
  throw new Error("Review output parent must be an owner-only directory (mode 0700).");
111844
112724
  }
111845
- const segments = parent.toLowerCase().split(sep13);
112725
+ const segments = parent.toLowerCase().split(sep14);
111846
112726
  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
112727
  throw new Error("Review output cannot be placed in agent-native discovery or Skills installation paths.");
111848
112728
  }
111849
- for (let current = parent;; current = dirname36(current)) {
111850
- if (existsSync35(join61(current, ".git"))) {
112729
+ for (let current = parent;; current = dirname37(current)) {
112730
+ if (existsSync36(join62(current, ".git"))) {
111851
112731
  throw new Error("Review output cannot be placed inside a Git worktree.");
111852
112732
  }
111853
112733
  if (current === parse12(current).root)
@@ -111858,7 +112738,7 @@ function assertPrivateDestination(output2) {
111858
112738
  async function exportSkillVersionForReview(spec, output2, client2) {
111859
112739
  const { slug: slug2, version: version2 } = exactVersion(spec);
111860
112740
  const target = assertPrivateDestination(output2);
111861
- if (existsSync35(target))
112741
+ if (existsSync36(target))
111862
112742
  throw new Error("Review output already exists; choose a new filename.");
111863
112743
  const metadata = await client2.getSkillVersion(slug2, version2);
111864
112744
  if (!metadata)
@@ -111885,7 +112765,7 @@ async function exportSkillVersionForReview(spec, output2, client2) {
111885
112765
  }
111886
112766
  const fd = openSync25(target, constants22.O_WRONLY | constants22.O_CREAT | constants22.O_EXCL | constants22.O_NOFOLLOW, 384);
111887
112767
  try {
111888
- writeFileSync30(fd, bytes);
112768
+ writeFileSync31(fd, bytes);
111889
112769
  fsyncSync7(fd);
111890
112770
  if (fstatSync25(fd).size !== bytes.byteLength)
111891
112771
  throw new Error("Incomplete review output write.");
@@ -111923,12 +112803,12 @@ function registerRegistry(parent) {
111923
112803
  async function writeJson2(value, space) {
111924
112804
  const text4 = `${JSON.stringify(value, null, space)}
111925
112805
  `;
111926
- await new Promise((resolve27, reject2) => {
112806
+ await new Promise((resolve28, reject2) => {
111927
112807
  process.stdout.write(text4, (error61) => {
111928
112808
  if (error61)
111929
112809
  reject2(error61);
111930
112810
  else
111931
- resolve27();
112811
+ resolve28();
111932
112812
  });
111933
112813
  });
111934
112814
  }
@@ -112132,9 +113012,9 @@ __export(exports_publish, {
112132
113012
  PushSkillError: () => PushSkillError
112133
113013
  });
112134
113014
  import { execFileSync as execFileSync2 } from "child_process";
112135
- import { existsSync as existsSync36, readFileSync as readFileSync43 } from "fs";
113015
+ import { existsSync as existsSync37, readFileSync as readFileSync44 } from "fs";
112136
113016
  import { hostname as hostname5 } from "os";
112137
- import { join as join62 } from "path";
113017
+ import { join as join63 } from "path";
112138
113018
  function registerPublish(parent) {
112139
113019
  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
113020
  try {
@@ -112186,8 +113066,8 @@ async function pushSkill(name, options = {}) {
112186
113066
  const packed = packSkillBundle(skill.path, { maxUnpackedBytes: MAX_UNPACKED_BYTES });
112187
113067
  await verifyPackedCanonicalHash(skill.name, packed);
112188
113068
  const versionManifest = buildVersionManifest(skill.path, packed);
112189
- const skillMdPath = join62(skill.path, "SKILL.md");
112190
- const skillMd = existsSync36(skillMdPath) ? readFileSync43(skillMdPath, "utf-8") : undefined;
113069
+ const skillMdPath = join63(skill.path, "SKILL.md");
113070
+ const skillMd = existsSync37(skillMdPath) ? readFileSync44(skillMdPath, "utf-8") : undefined;
112191
113071
  const base3 = {
112192
113072
  slug: skill.name,
112193
113073
  path: skill.path,
@@ -112425,7 +113305,7 @@ var exports_private_publications = {};
112425
113305
  __export(exports_private_publications, {
112426
113306
  registerPrivatePublications: () => registerPrivatePublications
112427
113307
  });
112428
- import { resolve as resolve27 } from "path";
113308
+ import { resolve as resolve28 } from "path";
112429
113309
  function verifyOptions(options, action) {
112430
113310
  if (options.userId === undefined !== (options.membershipId === undefined) || options.userId !== undefined && (!publicationUuid(options.userId) || !publicationUuid(options.membershipId)))
112431
113311
  throw new PrivatePublicationError("PUBLICATION_CONTEXT_REQUIRED", "Provide both observed --user-id and --membership-id UUIDs.");
@@ -112470,9 +113350,9 @@ function registerPrivatePublications(parent) {
112470
113350
  if (code2 === null)
112471
113351
  return;
112472
113352
  const context2 = options.userId ? { userId: options.userId, membershipId: options.membershipId } : undefined;
112473
- const client2 = await privatePublicationSession(options.email, code2, context2), directory2 = resolve27(options.recoveryDir);
113353
+ const client2 = await privatePublicationSession(options.email, code2, context2), directory2 = resolve28(options.recoveryDir);
112474
113354
  if (action === "publish")
112475
- await preparePrivatePublication(client2, resolve27(args2[0]), directory2, { skillId: options.skillId, expectedCurrentVersionId: options.expectedCurrentVersion ?? null, idempotencyKey: options.idempotencyKey });
113355
+ await preparePrivatePublication(client2, resolve28(args2[0]), directory2, { skillId: options.skillId, expectedCurrentVersionId: options.expectedCurrentVersion ?? null, idempotencyKey: options.idempotencyKey });
112476
113356
  const result2 = action === "publish" || action === "resume" ? await continuePrivatePublication(client2, directory2, { confirm: true, waitMs: Number(options.waitSeconds) * 1000 }) : await inspectPrivatePublication(client2, directory2, action === "cancel");
112477
113357
  print2(result2, options.json);
112478
113358
  } catch (error61) {
@@ -112574,7 +113454,7 @@ __export(exports_auth, {
112574
113454
  import { createInterface as createInterface3 } from "readline";
112575
113455
  function prompt(question) {
112576
113456
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
112577
- return new Promise((resolve28) => {
113457
+ return new Promise((resolve29) => {
112578
113458
  let settled = false;
112579
113459
  const finish = (answer) => {
112580
113460
  if (settled)
@@ -112583,7 +113463,7 @@ function prompt(question) {
112583
113463
  rl.close();
112584
113464
  if (answer === null)
112585
113465
  process.exitCode = 130;
112586
- resolve28(answer);
113466
+ resolve29(answer);
112587
113467
  };
112588
113468
  rl.once("SIGINT", () => finish(null));
112589
113469
  rl.once("close", () => finish(null));
@@ -112761,7 +113641,7 @@ Skipped. Run it any time: skills setup agents`));
112761
113641
  function promptHidden(question) {
112762
113642
  const stdin = process.stdin;
112763
113643
  process.stdout.write(question);
112764
- return new Promise((resolve28) => {
113644
+ return new Promise((resolve29) => {
112765
113645
  let value = "";
112766
113646
  const done = (answer) => {
112767
113647
  stdin.off("data", onData);
@@ -112771,7 +113651,7 @@ function promptHidden(question) {
112771
113651
  `);
112772
113652
  if (answer === null)
112773
113653
  process.exitCode = 130;
112774
- resolve28(answer);
113654
+ resolve29(answer);
112775
113655
  };
112776
113656
  const onData = (chunk2) => {
112777
113657
  for (const character of chunk2.toString()) {
@@ -113317,7 +114197,7 @@ function promptInvitationProof(kind) {
113317
114197
  const label = kind === "token" ? "invitation token" : "recovery code";
113318
114198
  const character = kind === "token" ? /^[A-Za-z0-9_-]$/ : /^\d$/;
113319
114199
  const { stdin, stderr: output2 } = process, wasRaw = stdin.isRaw, wasFlowing = stdin.readableFlowing;
113320
- return new Promise((resolve28) => {
114200
+ return new Promise((resolve29) => {
113321
114201
  let value = "", settled = false, overflow = false;
113322
114202
  const finish = (answer) => {
113323
114203
  if (settled)
@@ -113334,7 +114214,7 @@ function promptInvitationProof(kind) {
113334
114214
  `);
113335
114215
  if (answer === null)
113336
114216
  process.exitCode = 130;
113337
- resolve28(answer);
114217
+ resolve29(answer);
113338
114218
  };
113339
114219
  const cancel = () => finish(null);
113340
114220
  const keypress = (text4, key) => {
@@ -113906,8 +114786,8 @@ var init_storage2 = __esm(() => {
113906
114786
  });
113907
114787
 
113908
114788
  // 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";
114789
+ import { existsSync as existsSync38, readFileSync as readFileSync45, statSync as statSync24, writeFileSync as writeFileSync32 } from "fs";
114790
+ import { join as join64 } from "path";
113911
114791
  function isDirectory2(path) {
113912
114792
  try {
113913
114793
  return statSync24(path).isDirectory();
@@ -113918,15 +114798,15 @@ function isDirectory2(path) {
113918
114798
  function migrationNeeded(options) {
113919
114799
  if (options.rootDir)
113920
114800
  return false;
113921
- const appDir = options.homeDir ? join63(options.homeDir, ".hasna", "skills") : getDataDir();
113922
- return !(isOwnerLayoutMigrated(appDir) && isDirectory2(join63(appDir, SKILLS_CACHE_DIRNAME)));
114801
+ const appDir = options.homeDir ? join64(options.homeDir, ".hasna", "skills") : getDataDir();
114802
+ return !(isOwnerLayoutMigrated(appDir) && isDirectory2(join64(appDir, SKILLS_CACHE_DIRNAME)));
113923
114803
  }
113924
114804
  function readBaseline(skillDir) {
113925
- const markerPath = join63(skillDir, PULL_MARKER_FILE);
113926
- if (!existsSync37(markerPath))
114805
+ const markerPath = join64(skillDir, PULL_MARKER_FILE);
114806
+ if (!existsSync38(markerPath))
113927
114807
  return;
113928
114808
  try {
113929
- const marker = JSON.parse(readFileSync44(markerPath, "utf-8"));
114809
+ const marker = JSON.parse(readFileSync45(markerPath, "utf-8"));
113930
114810
  if (!isSkillsOwnershipMarker(marker))
113931
114811
  return;
113932
114812
  return {
@@ -113938,11 +114818,11 @@ function readBaseline(skillDir) {
113938
114818
  }
113939
114819
  }
113940
114820
  function readCursor(root2) {
113941
- const path = join63(root2, SYNC_CURSOR_FILE);
113942
- if (!existsSync37(path))
114821
+ const path = join64(root2, SYNC_CURSOR_FILE);
114822
+ if (!existsSync38(path))
113943
114823
  return { runCount: 0 };
113944
114824
  try {
113945
- const cursor2 = JSON.parse(readFileSync44(path, "utf-8"));
114825
+ const cursor2 = JSON.parse(readFileSync45(path, "utf-8"));
113946
114826
  return { runCount: typeof cursor2.runCount === "number" ? cursor2.runCount : 0 };
113947
114827
  } catch {
113948
114828
  return { runCount: 0 };
@@ -113951,12 +114831,12 @@ function readCursor(root2) {
113951
114831
  function resolveCorpusRootReadOnly(options) {
113952
114832
  if (options.rootDir)
113953
114833
  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);
114834
+ const appDir = options.homeDir ? join64(options.homeDir, ".hasna", "skills") : getDataDirReadOnly();
114835
+ const cache3 = join64(appDir, SKILLS_CACHE_DIRNAME);
113956
114836
  if (isOwnerLayoutMigrated(appDir) && isDirectory2(cache3)) {
113957
114837
  return { root: cache3, migrationPending: false };
113958
114838
  }
113959
- return { root: join63(appDir, INSTALLED_SKILLS_DIRNAME), migrationPending: true };
114839
+ return { root: join64(appDir, INSTALLED_SKILLS_DIRNAME), migrationPending: true };
113960
114840
  }
113961
114841
  function remoteRowToSkill(record11) {
113962
114842
  const slug2 = typeof record11.slug === "string" ? record11.slug : typeof record11.name === "string" ? record11.name : undefined;
@@ -113970,7 +114850,7 @@ function remoteRowToSkill(record11) {
113970
114850
  }
113971
114851
  function recheckLocalSide(plannedLocal, localDir, ops = {
113972
114852
  pack: (dir) => packSkillBundle(dir).sha256,
113973
- exists: existsSync37
114853
+ exists: existsSync38
113974
114854
  }) {
113975
114855
  let localNow;
113976
114856
  try {
@@ -114090,7 +114970,7 @@ async function reconcileRegistry(options = {}) {
114090
114970
  for (const slug2 of allSlugs) {
114091
114971
  const local = locals.get(slug2);
114092
114972
  const remote = remotes.get(slug2);
114093
- const baseline = local ? readBaseline(join63(root2, slug2)) : undefined;
114973
+ const baseline = local ? readBaseline(join64(root2, slug2)) : undefined;
114094
114974
  const { state, reason } = classifySkill(local, remote, baseline);
114095
114975
  let { action, reason: actionReason } = resolveAction(state, direction, conflict);
114096
114976
  if (state === "remote-only" && isDigestless(remote)) {
@@ -114149,7 +115029,7 @@ async function reconcileRegistry(options = {}) {
114149
115029
  try {
114150
115030
  await pushSkill(slug2, { rootDir: root2, client: client2 });
114151
115031
  const pushed = locals.get(slug2);
114152
- writePullMarker(join63(root2, slug2), {
115032
+ writePullMarker(join64(root2, slug2), {
114153
115033
  skill: slug2,
114154
115034
  ...pushed?.version ? { version: pushed.version } : {},
114155
115035
  ...pushed?.sha256 ? { contentHash: pushed.sha256 } : {},
@@ -114221,7 +115101,7 @@ async function reconcileRegistry(options = {}) {
114221
115101
  runCount: readCursor(root2).runCount + 1,
114222
115102
  summary
114223
115103
  };
114224
- writeFileSync31(join63(root2, SYNC_CURSOR_FILE), `${JSON.stringify(cursor2, null, 2)}
115104
+ writeFileSync32(join64(root2, SYNC_CURSOR_FILE), `${JSON.stringify(cursor2, null, 2)}
114225
115105
  `);
114226
115106
  return {
114227
115107
  corpusRoot: root2,