@hasna/skills 0.10.1 → 0.10.3

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.1",
36871
+ version: "0.10.3",
36872
36872
  description: "Skills library for AI coding agents",
36873
36873
  type: "module",
36874
36874
  bin: {
@@ -49704,7 +49704,9 @@ var init_skill_version = __esm(() => {
49704
49704
 
49705
49705
  // src/lib/selection-cache.ts
49706
49706
  import { createHash as createHash7, randomUUID as randomUUID3 } from "crypto";
49707
- import { closeSync as closeSync6, constants as constants4, fstatSync as fstatSync6, fsyncSync, lstatSync as lstatSync9, mkdirSync as mkdirSync3, openSync as openSync6, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
49707
+ import { spawnSync as spawnSync2 } from "child_process";
49708
+ import { closeSync as closeSync6, constants as constants4, fstatSync as fstatSync6, fsyncSync, lstatSync as lstatSync9, mkdirSync as mkdirSync3, openSync as openSync6, readFileSync as readFileSync10, readlinkSync as readlinkSync2, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
49709
+ import { hostname } from "os";
49708
49710
  import { dirname as dirname8, join as join16, parse as parse2, resolve as resolve8 } from "path";
49709
49711
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
49710
49712
  function selectionCacheRoot(options = {}) {
@@ -49986,6 +49988,201 @@ function readSkillSessionSnapshot(sessionId, options = {}) {
49986
49988
  function skillSessionSnapshotBinding(snapshot) {
49987
49989
  return { sessionId: snapshot.sessionId, generation: snapshot.generation, receiptSha256: snapshot.receiptSha256 };
49988
49990
  }
49991
+ function localBootId() {
49992
+ try {
49993
+ if (process.platform === "linux") {
49994
+ const value = readFileSync10("/proc/sys/kernel/random/boot_id", "utf8").trim();
49995
+ return /^[a-f0-9-]{36}$/i.test(value) ? value : null;
49996
+ }
49997
+ if (process.platform === "darwin") {
49998
+ const result2 = spawnSync2("/usr/sbin/sysctl", ["-n", "kern.boottime"], { encoding: "utf8", timeout: 3000, env: { LC_ALL: "C" } });
49999
+ if (result2.status !== 0 || result2.error)
50000
+ return null;
50001
+ const seconds = /\bsec\s*=\s*(\d+)\b/.exec(result2.stdout)?.[1];
50002
+ return seconds ? `darwin:${seconds}` : null;
50003
+ }
50004
+ } catch {}
50005
+ return null;
50006
+ }
50007
+ function processStart(pid) {
50008
+ if (!Number.isSafeInteger(pid) || pid <= 0)
50009
+ return { state: "unknown" };
50010
+ if (process.platform === "linux") {
50011
+ try {
50012
+ const stat = readFileSync10(`/proc/${pid}/stat`, "utf8");
50013
+ const close = stat.lastIndexOf(")");
50014
+ const start = close < 0 ? undefined : stat.slice(close + 2).trim().split(/\s+/)[19];
50015
+ return start && /^\d+$/.test(start) ? { state: "alive", start: `linux:${start}` } : { state: "unknown" };
50016
+ } catch (error) {
50017
+ return { state: error.code === "ENOENT" ? "absent" : "unknown" };
50018
+ }
50019
+ }
50020
+ if (process.platform === "darwin") {
50021
+ const result2 = spawnSync2("/bin/ps", ["-p", String(pid), "-o", "lstart="], { encoding: "utf8", timeout: 3000, env: { LC_ALL: "C" } });
50022
+ if (result2.error || result2.signal)
50023
+ return { state: "unknown" };
50024
+ const start = result2.stdout.trim();
50025
+ if (result2.status === 1 && !start && !result2.stderr.trim())
50026
+ return { state: "absent" };
50027
+ return result2.status === 0 && start ? { state: "alive", start: `darwin:${start}` } : { state: "unknown" };
50028
+ }
50029
+ return { state: "unknown" };
50030
+ }
50031
+ function localLockIdentity() {
50032
+ const bootId = localBootId(), owner = processStart(process.pid);
50033
+ if (!bootId || owner.state !== "alive")
50034
+ return null;
50035
+ let pidNamespace;
50036
+ if (process.platform === "linux") {
50037
+ try {
50038
+ pidNamespace = readlinkSync2("/proc/self/ns/pid");
50039
+ } catch {
50040
+ return null;
50041
+ }
50042
+ if (!/^pid:\[\d+\]$/.test(pidNamespace))
50043
+ return null;
50044
+ }
50045
+ return { host: hostname(), bootId, pidNamespace, processStart: owner.start };
50046
+ }
50047
+ function readSessionLock(sessionId, options) {
50048
+ const path = `${sessionReceiptPath(sessionId, options)}.write-lock`;
50049
+ const before2 = lstatSync9(path, { throwIfNoEntry: false });
50050
+ if (!before2)
50051
+ return null;
50052
+ if (!before2.isFile() || before2.nlink !== 1 || process.getuid && before2.uid !== process.getuid()) {
50053
+ throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is not a private regular file; recovery requires review.");
50054
+ }
50055
+ const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
50056
+ const after2 = lstatSync9(path, { throwIfNoEntry: false });
50057
+ if (!bytes || !after2?.isFile() || before2.dev !== after2.dev || before2.ino !== after2.ino || before2.mtimeMs !== after2.mtimeMs) {
50058
+ throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock changed during inspection; recovery requires review.");
50059
+ }
50060
+ let marker;
50061
+ try {
50062
+ marker = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
50063
+ } catch {
50064
+ throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is unreadable; recovery requires review.");
50065
+ }
50066
+ if (!marker || ![1, 2].includes(marker.schemaVersion) || marker.sessionId !== sessionId || !/^[a-f0-9-]{36}$/i.test(marker.operationId) || !Number.isSafeInteger(marker.pid) || marker.pid <= 0 || marker.schemaVersion === 2 && (!marker.host || !marker.bootId || !marker.processStart || process.platform === "linux" && !marker.pidNamespace)) {
50067
+ throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock has invalid ownership metadata; recovery requires review.");
50068
+ }
50069
+ return { path, bytes, marker, sha256: sha256Hex(bytes), dev: after2.dev, ino: after2.ino, mtimeMs: after2.mtimeMs };
50070
+ }
50071
+ function writerIsGone(lock, explicitLocalHost) {
50072
+ if (Date.now() - lock.mtimeMs < SESSION_LOCK_MIN_AGE_MS)
50073
+ return false;
50074
+ const owner = processStart(lock.marker.pid);
50075
+ if (owner.state === "unknown")
50076
+ return false;
50077
+ if (lock.marker.schemaVersion === 1)
50078
+ return explicitLocalHost && owner.state === "absent";
50079
+ const local = localLockIdentity();
50080
+ if (!local || lock.marker.host !== local.host || lock.marker.bootId !== local.bootId || lock.marker.pidNamespace !== local.pidNamespace)
50081
+ return false;
50082
+ return owner.state === "absent" || owner.state === "alive" && owner.start !== lock.marker.processStart;
50083
+ }
50084
+ function lockReviewDigest(lock, receipt) {
50085
+ return hash({
50086
+ sessionId: lock.marker.sessionId,
50087
+ lockSha256: lock.sha256,
50088
+ lockDev: lock.dev,
50089
+ lockIno: lock.ino,
50090
+ lockMtimeMs: lock.mtimeMs,
50091
+ receiptSha256: receipt?.sha256 ?? null,
50092
+ receiptGeneration: receipt?.generation ?? null
50093
+ });
50094
+ }
50095
+ function inspectSessionWriteLock(sessionId, options = {}) {
50096
+ const receipt = readSkillSessionSnapshotIfExists(sessionId, options), lock = readSessionLock(sessionId, options);
50097
+ if (!lock)
50098
+ throw new SkillSelectionError("SESSION_WRITE_LOCK_NOT_FOUND", "The session has no write lock to recover.");
50099
+ return {
50100
+ sessionId,
50101
+ schemaVersion: lock.marker.schemaVersion,
50102
+ pid: lock.marker.pid,
50103
+ lockSha256: lock.sha256,
50104
+ lockMtime: new Date(lock.mtimeMs).toISOString(),
50105
+ receiptSha256: receipt?.sha256 ?? null,
50106
+ receiptGeneration: receipt?.generation ?? null,
50107
+ reviewDigest: lockReviewDigest(lock, receipt),
50108
+ hostBound: lock.marker.schemaVersion === 2
50109
+ };
50110
+ }
50111
+ function archiveSessionLock(lock, receipt, explicitLocalHost) {
50112
+ const guardPath = `${lock.path}.recovery-guard`;
50113
+ let guardFd;
50114
+ try {
50115
+ guardFd = openSync6(guardPath, constants4.O_WRONLY | constants4.O_CREAT | constants4.O_EXCL | constants4.O_NOFOLLOW, 384);
50116
+ } catch (error) {
50117
+ if (error.code === "EEXIST")
50118
+ return null;
50119
+ throw error;
50120
+ }
50121
+ const guardStat = fstatSync6(guardFd);
50122
+ try {
50123
+ writeFileSync3(guardFd, `${JSON.stringify({ schemaVersion: 1, pid: process.pid, operationId: randomUUID3() })}
50124
+ `);
50125
+ fsyncSync(guardFd);
50126
+ const cacheOptions = { cacheDir: dirname8(dirname8(lock.path)) };
50127
+ const current = readSessionLock(lock.marker.sessionId, cacheOptions);
50128
+ if (!current || current.dev !== lock.dev || current.ino !== lock.ino || current.sha256 !== lock.sha256 || current.mtimeMs !== lock.mtimeMs || !writerIsGone(current, explicitLocalHost))
50129
+ return null;
50130
+ const currentReceipt = readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions);
50131
+ if (currentReceipt?.sha256 !== receipt?.sha256 || currentReceipt?.generation !== receipt?.generation)
50132
+ return null;
50133
+ const directory = join16(dirname8(dirname8(lock.path)), "session-lock-recoveries", randomUUID3());
50134
+ const archivePath = join16(directory, "original.write-lock");
50135
+ const recordPath = join16(directory, "recovery.json");
50136
+ assertRegularPath(archivePath, true);
50137
+ archiveBytes(recordPath, Buffer.from(`${JSON.stringify({
50138
+ schemaVersion: 1,
50139
+ sessionId: lock.marker.sessionId,
50140
+ originalPath: lock.path,
50141
+ lockSha256: lock.sha256,
50142
+ lockMtime: new Date(lock.mtimeMs).toISOString(),
50143
+ lockPid: lock.marker.pid,
50144
+ receiptSha256: receipt?.sha256 ?? null,
50145
+ receiptGeneration: receipt?.generation ?? null,
50146
+ status: "prepared"
50147
+ })}
50148
+ `));
50149
+ syncDirectory(directory);
50150
+ syncDirectory(dirname8(directory));
50151
+ const finalLock = readSessionLock(lock.marker.sessionId, cacheOptions);
50152
+ if (!finalLock || finalLock.dev !== lock.dev || finalLock.ino !== lock.ino || finalLock.sha256 !== lock.sha256 || readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions)?.sha256 !== receipt?.sha256)
50153
+ return null;
50154
+ let moved = false;
50155
+ try {
50156
+ renameSync(lock.path, archivePath);
50157
+ moved = true;
50158
+ syncDirectory(directory);
50159
+ syncDirectory(dirname8(lock.path));
50160
+ if (sha256Hex(readRegularFile(archivePath, SESSION_LOCK_MAX_BYTES)) !== lock.sha256) {
50161
+ throw new Error("Archived lock bytes changed");
50162
+ }
50163
+ } catch (error) {
50164
+ if (moved)
50165
+ throw new SkillSelectionError("SESSION_LOCK_RECOVERY_INCOMPLETE", "The lock moved, but durable recovery could not be confirmed; inspect the preserved recovery directory and session receipt.");
50166
+ throw error;
50167
+ }
50168
+ return archivePath;
50169
+ } finally {
50170
+ closeSync6(guardFd);
50171
+ const current = lstatSync9(guardPath, { throwIfNoEntry: false });
50172
+ if (current?.isFile() && current.dev === guardStat.dev && current.ino === guardStat.ino)
50173
+ unlinkSync(guardPath);
50174
+ }
50175
+ }
50176
+ function recoverSessionWriteLock(sessionId, reviewDigest, options = {}) {
50177
+ const receipt = readSkillSessionSnapshotIfExists(sessionId, options), lock = readSessionLock(sessionId, options);
50178
+ if (!lock || lockReviewDigest(lock, receipt) !== reviewDigest) {
50179
+ throw new SkillSelectionError("SESSION_LOCK_REVIEW_CHANGED", "The lock or receipt changed after review; inspect the session lock again.");
50180
+ }
50181
+ const archivePath = archiveSessionLock(lock, receipt, true);
50182
+ if (!archivePath)
50183
+ throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The lock is live, too recent, or changed during recovery; leave it in place and inspect again.");
50184
+ return archivePath;
50185
+ }
49989
50186
  function withSessionWriteLocks(sessionIds, options, action) {
49990
50187
  const operationId = randomUUID3();
49991
50188
  const locks = [...new Set(sessionIds)].map((sessionId) => ({ sessionId, path: `${sessionReceiptPath(sessionId, options)}.write-lock` })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
@@ -50005,13 +50202,30 @@ function withSessionWriteLocks(sessionIds, options, action) {
50005
50202
  try {
50006
50203
  fd = openSync6(lock.path, constants4.O_WRONLY | constants4.O_CREAT | constants4.O_EXCL | constants4.O_NOFOLLOW, 384);
50007
50204
  } catch (error) {
50008
- if (error.code === "EEXIST")
50009
- throw new SkillSelectionError("SESSION_WRITE_LOCKED", "Another Skills process owns a required session write lock; retry after it completes. A surviving lock requires explicit recovery review.");
50010
- throw error;
50205
+ if (error.code !== "EEXIST")
50206
+ throw error;
50207
+ try {
50208
+ const stale = readSessionLock(lock.sessionId, options);
50209
+ const receipt = stale && readSkillSessionSnapshotIfExists(lock.sessionId, options);
50210
+ if (stale?.marker.schemaVersion === 2 && writerIsGone(stale, false)) {
50211
+ archiveSessionLock(stale, receipt, false);
50212
+ }
50213
+ } catch (recoveryError) {
50214
+ if (recoveryError instanceof SkillSelectionError && recoveryError.code === "SESSION_LOCK_RECOVERY_INCOMPLETE")
50215
+ throw recoveryError;
50216
+ }
50217
+ try {
50218
+ fd = openSync6(lock.path, constants4.O_WRONLY | constants4.O_CREAT | constants4.O_EXCL | constants4.O_NOFOLLOW, 384);
50219
+ } catch (retryError) {
50220
+ if (retryError.code === "EEXIST")
50221
+ throw new SkillSelectionError("SESSION_WRITE_LOCKED", "Another Skills process owns a required session write lock; retry after it completes. A surviving legacy lock requires explicit recovery review.");
50222
+ throw retryError;
50223
+ }
50011
50224
  }
50012
50225
  const stat = fstatSync6(fd);
50013
50226
  owned.push({ path: lock.path, fd, dev: stat.dev, ino: stat.ino });
50014
- writeFileSync3(fd, `${JSON.stringify({ schemaVersion: 1, operationId, sessionId: lock.sessionId, pid: process.pid })}
50227
+ const identity4 = localLockIdentity();
50228
+ writeFileSync3(fd, `${JSON.stringify({ schemaVersion: identity4 ? 2 : 1, operationId, sessionId: lock.sessionId, pid: process.pid, ...identity4 })}
50015
50229
  `);
50016
50230
  }
50017
50231
  return action(assertOwned);
@@ -50128,7 +50342,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
50128
50342
  return { archivePath, receiptPath, beforeSha256: before2.sha256, afterSha256 };
50129
50343
  });
50130
50344
  }
50131
- var SkillSelectionError, MAX_CACHED_PROFILE_AGE_MS, SELECTION_LOCK_FILE = "selection.lock.json";
50345
+ var SkillSelectionError, MAX_CACHED_PROFILE_AGE_MS, SELECTION_LOCK_FILE = "selection.lock.json", SESSION_LOCK_MAX_BYTES = 4096, SESSION_LOCK_MIN_AGE_MS = 30000;
50132
50346
  var init_selection_cache = __esm(() => {
50133
50347
  init_config();
50134
50348
  init_skill_bundle();
@@ -50186,7 +50400,7 @@ function capturePreparationProcess(child, platform2 = process.platform, signalGr
50186
50400
  }
50187
50401
 
50188
50402
  // src/lib/dependency-preparation.ts
50189
- import { constants as constants5, closeSync as closeSync7, fstatSync as fstatSync7, lstatSync as lstatSync10, mkdirSync as mkdirSync4, openSync as openSync7, readFileSync as readFileSync10, readdirSync as readdirSync5, rmdirSync, unlinkSync as unlinkSync2, ftruncateSync, writeSync } from "fs";
50403
+ import { constants as constants5, closeSync as closeSync7, fstatSync as fstatSync7, lstatSync as lstatSync10, mkdirSync as mkdirSync4, openSync as openSync7, readFileSync as readFileSync11, readdirSync as readdirSync5, rmdirSync, unlinkSync as unlinkSync2, ftruncateSync, writeSync } from "fs";
50190
50404
  import { join as join17 } from "path";
50191
50405
  function hasDependencyPreparationMarker(skillPath) {
50192
50406
  try {
@@ -50216,7 +50430,7 @@ function claimPreparation(skillPath) {
50216
50430
  return;
50217
50431
  if (!lstatSync10(state).isFile() || lstatSync10(state).isSymbolicLink() || lstatSync10(state).size > 1024)
50218
50432
  return;
50219
- const previous = JSON.parse(readFileSync10(state, "utf8"));
50433
+ const previous = JSON.parse(readFileSync11(state, "utf8"));
50220
50434
  if (previous.version !== 1 || previous.status !== "failed" || Object.keys(previous).length !== 2)
50221
50435
  return;
50222
50436
  }
@@ -50436,7 +50650,7 @@ var init_managed_policy = __esm(() => {
50436
50650
 
50437
50651
  // src/lib/agent-bridge.ts
50438
50652
  import { createHash as createHash8 } from "crypto";
50439
- import { lstatSync as lstatSync12, readFileSync as readFileSync11, readdirSync as readdirSync6 } from "fs";
50653
+ import { lstatSync as lstatSync12, readFileSync as readFileSync12, readdirSync as readdirSync6 } from "fs";
50440
50654
  import { join as join19 } from "path";
50441
50655
  function isOwnedCliBridge(path, expectedPaths) {
50442
50656
  if (!expectedPaths.includes(path))
@@ -50448,7 +50662,7 @@ function isOwnedCliBridge(path, expectedPaths) {
50448
50662
  return false;
50449
50663
  return Object.entries(CLI_BRIDGE_FILES).every(([name, content]) => {
50450
50664
  const file = join19(path, name), stat = lstatSync12(file);
50451
- return stat.isFile() && !stat.isSymbolicLink() && stat.size === Buffer.byteLength(content) && readFileSync11(file, "utf8") === content;
50665
+ return stat.isFile() && !stat.isSymbolicLink() && stat.size === Buffer.byteLength(content) && readFileSync12(file, "utf8") === content;
50452
50666
  });
50453
50667
  } catch {
50454
50668
  return false;
@@ -50822,7 +51036,7 @@ var init_claude_settings_witness = __esm(() => {
50822
51036
 
50823
51037
  // src/lib/agent-integration.ts
50824
51038
  import { createHash as createHash10, randomUUID as randomUUID5 } from "crypto";
50825
- import { existsSync as existsSync12, lstatSync as lstatSync14, statSync as statSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync12, readdirSync as readdirSync7, opendirSync as opendirSync3, readlinkSync as readlinkSync2, realpathSync as realpathSync3, renameSync as renameSync2, rmdirSync as rmdirSync2, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, chmodSync, openSync as openSync10, closeSync as closeSync10, fsyncSync as fsyncSync2, fstatSync as fstatSync10, readSync as readSync6, constants as constants8, linkSync } from "fs";
51039
+ import { existsSync as existsSync12, lstatSync as lstatSync14, statSync as statSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync13, readdirSync as readdirSync7, opendirSync as opendirSync3, readlinkSync as readlinkSync3, realpathSync as realpathSync3, renameSync as renameSync2, rmdirSync as rmdirSync2, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, chmodSync, openSync as openSync10, closeSync as closeSync10, fsyncSync as fsyncSync2, fstatSync as fstatSync10, readSync as readSync6, constants as constants8, linkSync } from "fs";
50826
51040
  import { dirname as dirname10, join as join20, resolve as resolve10, relative as relative4, isAbsolute as isAbsolute9, sep as sep4 } from "path";
50827
51041
  import { homedir as homedir3 } from "os";
50828
51042
  function assertSafePath(path) {
@@ -50842,7 +51056,7 @@ function readRootAlias(home, agent) {
50842
51056
  const stat = lstatSync14(alias, { throwIfNoEntry: false, bigint: true });
50843
51057
  if (!stat?.isSymbolicLink())
50844
51058
  throw new Error(`Agent root alias changed after planning: ${alias}`);
50845
- const link2 = readlinkSync2(alias), targetPath = resolve10(dirname10(alias), link2);
51059
+ const link2 = readlinkSync3(alias), targetPath = resolve10(dirname10(alias), link2);
50846
51060
  assertSafePath(targetPath);
50847
51061
  const targetStat = lstatSync14(targetPath, { throwIfNoEntry: false });
50848
51062
  if (!targetStat?.isDirectory())
@@ -50950,7 +51164,7 @@ function parseNativeMigrationTargetManifest(input) {
50950
51164
  if (!target || typeof target !== "object" || Array.isArray(target))
50951
51165
  throw new Error("Invalid native migration target");
50952
51166
  const item = target;
50953
- if (Object.keys(item).some((key) => !["agent", "projectRoot", "path", "treeSha256"].includes(key)) || typeof item.agent !== "string" || typeof item.projectRoot !== "string" || typeof item.path !== "string" || typeof item.treeSha256 !== "string")
51167
+ if (Object.keys(item).some((key) => !["agent", "projectRoot", "path", "treeSha256", "vendor"].includes(key)) || typeof item.agent !== "string" || typeof item.projectRoot !== "string" || typeof item.path !== "string" || typeof item.treeSha256 !== "string" || item.vendor !== undefined && item.vendor !== true)
50954
51168
  throw new Error("Invalid native migration target fields");
50955
51169
  if (!ROOTS.some(([agent]) => agent === item.agent))
50956
51170
  throw new Error(`Unsupported native migration target agent: ${item.agent}`);
@@ -50969,7 +51183,7 @@ function parseNativeMigrationTargetManifest(input) {
50969
51183
  if (identities.has(identity4))
50970
51184
  throw new Error(`Duplicate native migration target: ${item.path}`);
50971
51185
  identities.add(identity4);
50972
- targets.push({ agent: item.agent, projectRoot, path: normalized, treeSha256: item.treeSha256 });
51186
+ targets.push({ agent: item.agent, projectRoot, path: normalized, treeSha256: item.treeSha256, ...item.vendor === true ? { vendor: true } : {} });
50973
51187
  }
50974
51188
  return { schema: "hasna.skills-native-migration-targets.v1", targets, digest };
50975
51189
  }
@@ -50997,8 +51211,8 @@ function selectNativeMigrationTargets(inventory, manifest) {
50997
51211
  if (matches2.length !== 1)
50998
51212
  throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
50999
51213
  const entry = matches2[0];
51000
- if (entry.bridge || entry.system || entry.vendor)
51001
- throw new Error(`Native migration target is protected or vendor-owned: ${entry.path}`);
51214
+ if (entry.bridge || entry.system || entry.vendor !== (target.vendor === true))
51215
+ throw new Error(`Native migration target is protected or vendor classification changed: ${entry.path}`);
51002
51216
  if (entry.hash !== target.treeSha256 || treeHash(entry.path) !== target.treeSha256)
51003
51217
  throw new Error(`Native migration target changed after review: ${entry.path}`);
51004
51218
  selected.push(entry);
@@ -51021,6 +51235,8 @@ function projectAncestorDirectories(projects) {
51021
51235
  return [...directories];
51022
51236
  }
51023
51237
  function inventoryNativeSkills(home = homedir3(), options = {}) {
51238
+ if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute9(options.reviewedCacheAlias) || resolve10(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
51239
+ throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
51024
51240
  const aliases = rootAliases(home, options.allowRootAliases);
51025
51241
  const selectedAgents = options.agents ? new Set(options.agents) : undefined;
51026
51242
  const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
@@ -51041,14 +51257,22 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
51041
51257
  if (discoveryPathBytes > 4 * 1024 * 1024)
51042
51258
  throw new Error("Native skill discovery metadata limit exceeded");
51043
51259
  }
51044
- function emptySiblingCacheAlias(path, parent) {
51045
- const link2 = readlinkSync2(path), target = resolve10(parent, link2);
51260
+ let reviewedCacheAliasFound = false;
51261
+ function verifiedSiblingCacheAlias(path, parent) {
51262
+ const link2 = readlinkSync3(path), target = resolve10(parent, link2);
51046
51263
  if (dirname10(target) !== parent || (isAbsolute9(link2) ? link2 !== target : dirname10(link2) !== "."))
51047
51264
  return false;
51048
51265
  if (!lstatSync14(target, { throwIfNoEntry: false })?.isDirectory())
51049
51266
  return false;
51050
51267
  const scan = cacheScans.get(target);
51051
- return Boolean(scan?.complete && !scan.hasSkills && scan.entries <= maxAliasProofEntries);
51268
+ if (!scan?.complete || scan.entries > maxAliasProofEntries)
51269
+ return false;
51270
+ if (scan.hasSkills) {
51271
+ if (path !== options.reviewedCacheAlias)
51272
+ return false;
51273
+ reviewedCacheAliasFound = true;
51274
+ }
51275
+ return true;
51052
51276
  }
51053
51277
  function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false) {
51054
51278
  if (!admitted)
@@ -51071,7 +51295,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
51071
51295
  const marker = join20(path, ".hasna-skills.json");
51072
51296
  if (existsSync12(marker)) {
51073
51297
  try {
51074
- managed = JSON.parse(readFileSync12(marker, "utf8")).managedBy === "@hasna/skills";
51298
+ managed = JSON.parse(readFileSync13(marker, "utf8")).managedBy === "@hasna/skills";
51075
51299
  } catch {}
51076
51300
  }
51077
51301
  const bridge = !vendor && isOwnedCliBridge(path, bridgePaths);
@@ -51105,7 +51329,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
51105
51329
  const child = join20(path, name);
51106
51330
  if (isVendor && lstatSync14(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
51107
51331
  const target = statSync7(child, { throwIfNoEntry: false });
51108
- if (target?.isFile() || pluginCache && target?.isDirectory() && emptySiblingCacheAlias(child, path)) {
51332
+ if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(child, path)) {
51109
51333
  scan.entries++;
51110
51334
  continue;
51111
51335
  }
@@ -51139,12 +51363,14 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
51139
51363
  for (const root of [...options.agentRoots ?? [], ...configured])
51140
51364
  if (includesAgent(root.agent))
51141
51365
  scanRoot(root.agent, canonicalAgentPath(root.path, aliases), true);
51366
+ if (options.reviewedCacheAlias !== undefined && !reviewedCacheAliasFound)
51367
+ throw new Error("Reviewed cache alias was not an inventoried skill-containing sibling alias");
51142
51368
  recheckRootAliases(aliases);
51143
51369
  return entries;
51144
51370
  }
51145
51371
  function readOptional(path) {
51146
51372
  assertSafePath(path);
51147
- return existsSync12(path) ? readFileSync12(path, "utf8") : null;
51373
+ return existsSync12(path) ? readFileSync13(path, "utf8") : null;
51148
51374
  }
51149
51375
  function jsonObject(text2, path) {
51150
51376
  if (text2 === null)
@@ -51945,7 +52171,7 @@ import {
51945
52171
  lstatSync as lstatSync15,
51946
52172
  mkdirSync as mkdirSync7,
51947
52173
  mkdtempSync,
51948
- readFileSync as readFileSync13,
52174
+ readFileSync as readFileSync14,
51949
52175
  readdirSync as readdirSync9,
51950
52176
  renameSync as renameSync4,
51951
52177
  rmSync as rmSync2,
@@ -51962,7 +52188,7 @@ function hasSkillsOwnershipMarker(dir) {
51962
52188
  try {
51963
52189
  if (!lstatSync15(path).isFile())
51964
52190
  return false;
51965
- const marker = JSON.parse(readFileSync13(path, "utf8"));
52191
+ const marker = JSON.parse(readFileSync14(path, "utf8"));
51966
52192
  return isSkillsOwnershipMarker(marker);
51967
52193
  } catch {
51968
52194
  return false;
@@ -52170,7 +52396,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
52170
52396
  if (dirExists && managed && hasSkillMd && !options.force && isPointerSkillMd(skillMd)) {
52171
52397
  let existingIsStub = false;
52172
52398
  try {
52173
- existingIsStub = isPointerSkillMd(readFileSync13(skillMdPath, "utf-8"));
52399
+ existingIsStub = isPointerSkillMd(readFileSync14(skillMdPath, "utf-8"));
52174
52400
  } catch {
52175
52401
  existingIsStub = false;
52176
52402
  }
@@ -52243,7 +52469,7 @@ function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = f
52243
52469
  if (kind === undefined || kind === "instruction" || preferBundledDocs) {
52244
52470
  const skillMdPath = join23(skillPath, "SKILL.md");
52245
52471
  if (existsSync14(skillMdPath))
52246
- return readFileSync13(skillMdPath, "utf-8");
52472
+ return readFileSync14(skillMdPath, "utf-8");
52247
52473
  }
52248
52474
  return pointerSkillMd(name, description);
52249
52475
  }
@@ -52293,7 +52519,7 @@ var init_skill_aliases = __esm(() => {
52293
52519
  });
52294
52520
 
52295
52521
  // src/lib/project-state.ts
52296
- import { existsSync as existsSync15, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
52522
+ import { existsSync as existsSync15, mkdirSync as mkdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "fs";
52297
52523
  import { join as join25 } from "path";
52298
52524
  function getProjectStateDir(targetDir = process.cwd()) {
52299
52525
  return join25(targetDir, SKILLS_PROJECT_DIR);
@@ -52306,7 +52532,7 @@ function loadProjectConfig(targetDir = process.cwd()) {
52306
52532
  if (!existsSync15(path))
52307
52533
  return null;
52308
52534
  try {
52309
- return normalizeProjectConfig(JSON.parse(readFileSync14(path, "utf-8")));
52535
+ return normalizeProjectConfig(JSON.parse(readFileSync15(path, "utf-8")));
52310
52536
  } catch {
52311
52537
  return null;
52312
52538
  }
@@ -52460,7 +52686,7 @@ __export(exports_installer, {
52460
52686
  AGENT_TARGETS: () => AGENT_TARGETS,
52461
52687
  AGENT_LABELS: () => AGENT_LABELS
52462
52688
  });
52463
- import { existsSync as existsSync16, readFileSync as readFileSync15, rmSync as rmSync3 } from "fs";
52689
+ import { existsSync as existsSync16, readFileSync as readFileSync16, rmSync as rmSync3 } from "fs";
52464
52690
  import { join as join26 } from "path";
52465
52691
  import { homedir as homedir5 } from "os";
52466
52692
  function getSkillPath(name) {
@@ -52559,7 +52785,7 @@ function createLocalSkillManifest(name, generateSkillMd) {
52559
52785
  let skillMd = "";
52560
52786
  const skillMdPath = join26(sourcePath, "SKILL.md");
52561
52787
  if (existsSync16(skillMdPath)) {
52562
- skillMd = readFileSync15(skillMdPath, "utf-8");
52788
+ skillMd = readFileSync16(skillMdPath, "utf-8");
52563
52789
  } else if (generateSkillMd) {
52564
52790
  skillMd = generateSkillMd(name) ?? "";
52565
52791
  } else {
@@ -52686,7 +52912,7 @@ function resolveAgentSkillMd(name, generateSkillMd) {
52686
52912
  const sourcePath = getSkillPath(name);
52687
52913
  const skillMdPath = join26(sourcePath, "SKILL.md");
52688
52914
  if (existsSync16(skillMdPath))
52689
- return readFileSync15(skillMdPath, "utf-8");
52915
+ return readFileSync16(skillMdPath, "utf-8");
52690
52916
  if (generateSkillMd)
52691
52917
  return generateSkillMd(name);
52692
52918
  return generateMinimalSkillMd(name);
@@ -52742,14 +52968,14 @@ function readBundledSkillVersion(name) {
52742
52968
  if (!existsSync16(pkgPath))
52743
52969
  return "unknown";
52744
52970
  try {
52745
- const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
52971
+ const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
52746
52972
  return pkg.version || "unknown";
52747
52973
  } catch {
52748
52974
  return "unknown";
52749
52975
  }
52750
52976
  }
52751
52977
  function readFileIfExists(path) {
52752
- return existsSync16(path) ? readFileSync15(path, "utf-8") : null;
52978
+ return existsSync16(path) ? readFileSync16(path, "utf-8") : null;
52753
52979
  }
52754
52980
  function loadProjectConfigCompat(targetDir) {
52755
52981
  return loadProjectConfig(targetDir);
@@ -52824,7 +53050,7 @@ __export(exports_skillinfo, {
52824
53050
  generateEnvExample: () => generateEnvExample,
52825
53051
  detectProjectSkills: () => detectProjectSkills
52826
53052
  });
52827
- import { existsSync as existsSync17, readFileSync as readFileSync16 } from "fs";
53053
+ import { existsSync as existsSync17, readFileSync as readFileSync17 } from "fs";
52828
53054
  import { join as join27 } from "path";
52829
53055
  function isInstructionSkillDir(skillPath, meta) {
52830
53056
  if (meta?.kind === "instruction")
@@ -52833,7 +53059,7 @@ function isInstructionSkillDir(skillPath, meta) {
52833
53059
  if (!existsSync17(skillMdPath))
52834
53060
  return false;
52835
53061
  try {
52836
- return parseSkillFrontmatter(readFileSync16(skillMdPath, "utf-8"))?.kind === "instruction";
53062
+ return parseSkillFrontmatter(readFileSync17(skillMdPath, "utf-8"))?.kind === "instruction";
52837
53063
  } catch {
52838
53064
  return false;
52839
53065
  }
@@ -52868,7 +53094,7 @@ function getSkillRequirements(name) {
52868
53094
  const meta = getSkill(name);
52869
53095
  let dependencies = {};
52870
53096
  try {
52871
- dependencies = JSON.parse(readFileSync16(join27(skillPath, "package.json"), "utf8")).dependencies || {};
53097
+ dependencies = JSON.parse(readFileSync17(join27(skillPath, "package.json"), "utf8")).dependencies || {};
52872
53098
  } catch {}
52873
53099
  return getSkillRequirementsFromContent(name, texts, dependencies, meta);
52874
53100
  }
@@ -52957,7 +53183,7 @@ async function runSkill(name, args, options = {}) {
52957
53183
  }
52958
53184
  let entryPoint2;
52959
53185
  try {
52960
- const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
53186
+ const pkg = JSON.parse(readFileSync17(pkgPath, "utf-8"));
52961
53187
  if (pkg.bin) {
52962
53188
  const binValues = Object.values(pkg.bin);
52963
53189
  entryPoint2 = binValues[0];
@@ -53002,7 +53228,7 @@ async function runSkill(name, args, options = {}) {
53002
53228
  function detectProjectSkills(cwd2 = process.cwd()) {
53003
53229
  let pkg;
53004
53230
  try {
53005
- pkg = JSON.parse(readFileSync16(join27(cwd2, "package.json"), "utf8"));
53231
+ pkg = JSON.parse(readFileSync17(join27(cwd2, "package.json"), "utf8"));
53006
53232
  } catch {
53007
53233
  return { detected: [], recommended: [] };
53008
53234
  }
@@ -53088,7 +53314,7 @@ function generateSkillMd(name) {
53088
53314
  const pkgPath = join27(skillPath, "package.json");
53089
53315
  if (existsSync17(pkgPath)) {
53090
53316
  try {
53091
- const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
53317
+ const pkg = JSON.parse(readFileSync17(pkgPath, "utf-8"));
53092
53318
  if (pkg.bin) {
53093
53319
  const binKeys = Object.keys(pkg.bin);
53094
53320
  if (binKeys.length > 0)
@@ -53162,7 +53388,7 @@ function extractEnvVars(text2) {
53162
53388
  function readIfExists(path) {
53163
53389
  try {
53164
53390
  if (existsSync17(path)) {
53165
- return readFileSync16(path, "utf-8");
53391
+ return readFileSync17(path, "utf-8");
53166
53392
  }
53167
53393
  } catch {}
53168
53394
  return null;
@@ -55007,7 +55233,7 @@ var init_codex_hook_discovery_recovery = __esm(() => {
55007
55233
 
55008
55234
  // src/lib/agent-discovery.ts
55009
55235
  import { createHash as createHash16 } from "crypto";
55010
- import { lstatSync as lstatSync20, readFileSync as readFileSync17, readdirSync as readdirSync10, realpathSync as realpathSync4 } from "fs";
55236
+ import { lstatSync as lstatSync20, readFileSync as readFileSync18, readdirSync as readdirSync10, realpathSync as realpathSync4 } from "fs";
55011
55237
  import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute15, join as join31, resolve as resolve15, sep as sep5 } from "path";
55012
55238
  import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
55013
55239
  function parseConfig(text3, path, toml = false) {
@@ -55039,7 +55265,7 @@ function read2(path, changes) {
55039
55265
  return null;
55040
55266
  if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
55041
55267
  throw new Error(`Unsupported or oversized native discovery input: ${path}`);
55042
- return readFileSync17(path, "utf8");
55268
+ return readFileSync18(path, "utf8");
55043
55269
  }
55044
55270
  function projected(source, changes, budget = discoveryByteBudget()) {
55045
55271
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
@@ -56087,7 +56313,7 @@ __export(exports_registry, {
56087
56313
  CATEGORIES: () => CATEGORIES,
56088
56314
  BASIC_SKILL_NAMES: () => BASIC_SKILL_NAMES
56089
56315
  });
56090
- import { existsSync as existsSync19, readFileSync as readFileSync18, readdirSync as readdirSync12 } from "fs";
56316
+ import { existsSync as existsSync19, readFileSync as readFileSync19, readdirSync as readdirSync12 } from "fs";
56091
56317
  import { join as join34 } from "path";
56092
56318
  function isBasicSkillName(name) {
56093
56319
  return BASIC_SKILL_NAMES.includes(name);
@@ -56137,7 +56363,7 @@ function discoverSkillsInDir(dir, source = "custom") {
56137
56363
  continue;
56138
56364
  let content;
56139
56365
  try {
56140
- content = readFileSync18(skillMdPath, "utf-8");
56366
+ content = readFileSync19(skillMdPath, "utf-8");
56141
56367
  } catch {
56142
56368
  continue;
56143
56369
  }
@@ -56174,7 +56400,7 @@ function findExtensionSkillPath(name) {
56174
56400
  continue;
56175
56401
  let content;
56176
56402
  try {
56177
- content = readFileSync18(skillMdPath, "utf-8");
56403
+ content = readFileSync19(skillMdPath, "utf-8");
56178
56404
  } catch {
56179
56405
  continue;
56180
56406
  }
@@ -57886,7 +58112,7 @@ var require_cli_spinners = __commonJS((exports, module) => {
57886
58112
  });
57887
58113
 
57888
58114
  // src/lib/auth-store.ts
57889
- import { chmodSync as chmodSync2, existsSync as existsSync20, mkdirSync as mkdirSync11, readFileSync as readFileSync19, renameSync as renameSync7, statSync as statSync11, unlinkSync as unlinkSync5, writeFileSync as writeFileSync10 } from "fs";
58115
+ import { chmodSync as chmodSync2, existsSync as existsSync20, mkdirSync as mkdirSync11, readFileSync as readFileSync20, renameSync as renameSync7, statSync as statSync11, unlinkSync as unlinkSync5, writeFileSync as writeFileSync10 } from "fs";
57890
58116
  import { basename as basename11, dirname as dirname19, join as join35 } from "path";
57891
58117
  function getAuthFilePath(env3 = process.env) {
57892
58118
  return skillsCredentialFilePath(env3);
@@ -57900,7 +58126,7 @@ function getAuthIdentity(env3 = process.env) {
57900
58126
  }
57901
58127
  function readIdentity(env3 = process.env) {
57902
58128
  try {
57903
- const parsed = JSON.parse(readFileSync19(getIdentityFilePath(env3), "utf-8"));
58129
+ const parsed = JSON.parse(readFileSync20(getIdentityFilePath(env3), "utf-8"));
57904
58130
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
57905
58131
  return {};
57906
58132
  const record2 = parsed;
@@ -57931,7 +58157,7 @@ function writeCredentialValues(values2, env3 = process.env) {
57931
58157
  const lines = [];
57932
58158
  const written = new Set;
57933
58159
  if (existsSync20(file)) {
57934
- for (const raw of readFileSync19(file, "utf-8").split(/\r?\n/)) {
58160
+ for (const raw of readFileSync20(file, "utf-8").split(/\r?\n/)) {
57935
58161
  const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(raw);
57936
58162
  const key = match?.[1];
57937
58163
  if (key && key in values2) {
@@ -57969,7 +58195,7 @@ function readCredentialValue(key, env3 = process.env) {
57969
58195
  if (!existsSync20(file))
57970
58196
  return null;
57971
58197
  try {
57972
- for (const raw of readFileSync19(file, "utf-8").split(/\r?\n/)) {
58198
+ for (const raw of readFileSync20(file, "utf-8").split(/\r?\n/)) {
57973
58199
  const match = new RegExp(`^\\s*(?:export\\s+)?${key}\\s*=\\s*(.*)$`).exec(raw);
57974
58200
  if (!match)
57975
58201
  continue;
@@ -57987,7 +58213,7 @@ function readCredentialValue(key, env3 = process.env) {
57987
58213
  }
57988
58214
  function readIdentityRecord(env3 = process.env) {
57989
58215
  try {
57990
- const parsed = JSON.parse(readFileSync19(getIdentityFilePath(env3), "utf-8"));
58216
+ const parsed = JSON.parse(readFileSync20(getIdentityFilePath(env3), "utf-8"));
57991
58217
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
57992
58218
  } catch {
57993
58219
  return null;
@@ -60276,7 +60502,7 @@ __export(exports_regexes, {
60276
60502
  idnEmail: () => idnEmail,
60277
60503
  httpProtocol: () => httpProtocol,
60278
60504
  html5Email: () => html5Email,
60279
- hostname: () => hostname,
60505
+ hostname: () => hostname2,
60280
60506
  hex: () => hex,
60281
60507
  guid: () => guid,
60282
60508
  extendedDuration: () => extendedDuration,
@@ -60336,7 +60562,7 @@ var cuid, cuid2, ulid, xid, ksuid, nanoid, duration, extendedDuration, guid, uui
60336
60562
  }, uuid42, uuid6, uuid7, email2, html5Email, rfc5322Email, unicodeEmail, idnEmail, browserEmail, _emoji = `^[\\p{Extended_Pictographic}\\p{Emoji_Component}]+$`, ipv4, ipv6, mac = (delimiter) => {
60337
60563
  const escapedDelim = escapeRegex(delimiter ?? ":");
60338
60564
  return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
60339
- }, cidrv4, cidrv6, base64, base64url, hostname, domain, httpProtocol, e164, creditCard, dateSource = `(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`, date2, string = (params) => {
60565
+ }, cidrv4, cidrv6, base64, base64url, hostname2, domain, httpProtocol, e164, creditCard, dateSource = `(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`, date2, string = (params) => {
60340
60566
  const regex2 = params ? `[\\s\\S]{${params?.minimum ?? 0},${params?.maximum ?? ""}}` : `[\\s\\S]*`;
60341
60567
  return new RegExp(`^${regex2}$`);
60342
60568
  }, bigint, integer, number, boolean, _null, _undefined, lowercase, uppercase, hex, md5_hex, md5_base64, md5_base64url, sha1_hex, sha1_base64, sha1_base64url, sha256_hex, sha256_base64, sha256_base64url, sha384_hex, sha384_base64, sha384_base64url, sha512_hex, sha512_base64, sha512_base64url;
@@ -60366,7 +60592,7 @@ var init_regexes = __esm(() => {
60366
60592
  cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;
60367
60593
  base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
60368
60594
  base64url = /^[A-Za-z0-9_-]*$/;
60369
- hostname = /^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/;
60595
+ hostname2 = /^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/;
60370
60596
  domain = /^(?=.{1,253}$)([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,63}$/;
60371
60597
  httpProtocol = /^https?$/;
60372
60598
  e164 = /^\+[1-9]\d{6,14}$/;
@@ -61018,9 +61244,9 @@ function parseURLObject(trimmed, def) {
61018
61244
  function stripTabAndNewline(value) {
61019
61245
  return value.replace(asciiTabOrNewline, "");
61020
61246
  }
61021
- function urlHostnameOk(url, hostname2) {
61022
- hostname2.lastIndex = 0;
61023
- return hostname2.test(url.hostname);
61247
+ function urlHostnameOk(url, hostname3) {
61248
+ hostname3.lastIndex = 0;
61249
+ return hostname3.test(url.hostname);
61024
61250
  }
61025
61251
  function urlProtocolOk(url, protocol) {
61026
61252
  protocol.lastIndex = 0;
@@ -75322,7 +75548,7 @@ __export(exports_schemas2, {
75322
75548
  int: () => int,
75323
75549
  instanceof: () => _instanceof,
75324
75550
  httpUrl: () => httpUrl,
75325
- hostname: () => hostname2,
75551
+ hostname: () => hostname3,
75326
75552
  hex: () => hex2,
75327
75553
  hash: () => hash4,
75328
75554
  guid: () => guid2,
@@ -75524,7 +75750,7 @@ function jwt(params) {
75524
75750
  function stringFormat(format, fnOrRegex, _params = {}) {
75525
75751
  return _stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params);
75526
75752
  }
75527
- function hostname2(_params) {
75753
+ function hostname3(_params) {
75528
75754
  return _stringFormat(ZodCustomStringFormat, "hostname", exports_regexes.hostname, _params);
75529
75755
  }
75530
75756
  function hex2(_params) {
@@ -77788,7 +78014,7 @@ __export(exports_external, {
77788
78014
  input: () => input,
77789
78015
  includes: () => _includes,
77790
78016
  httpUrl: () => httpUrl,
77791
- hostname: () => hostname2,
78017
+ hostname: () => hostname3,
77792
78018
  hex: () => hex2,
77793
78019
  hash: () => hash4,
77794
78020
  guid: () => guid2,
@@ -79916,7 +80142,7 @@ var init_remote_auth = __esm(() => {
79916
80142
 
79917
80143
  // src/lib/sign-in.ts
79918
80144
  import { createHash as createHash19 } from "crypto";
79919
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync12, readFileSync as readFileSync20, readdirSync as readdirSync13, renameSync as renameSync8, unlinkSync as unlinkSync6, writeFileSync as writeFileSync11 } from "fs";
80145
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync12, readFileSync as readFileSync21, readdirSync as readdirSync13, renameSync as renameSync8, unlinkSync as unlinkSync6, writeFileSync as writeFileSync11 } from "fs";
79920
80146
  import { basename as basename12, dirname as dirname20, join as join36 } from "path";
79921
80147
  function isRecord6(value) {
79922
80148
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -80092,7 +80318,7 @@ function loadPendingDeviceSignIn(origin, env3 = process.env, now3 = Date.now())
80092
80318
  let parsed;
80093
80319
  try {
80094
80320
  path = pendingDeviceSignInPath(origin, env3);
80095
- parsed = JSON.parse(readFileSync20(path, "utf8"));
80321
+ parsed = JSON.parse(readFileSync21(path, "utf8"));
80096
80322
  } catch {
80097
80323
  return null;
80098
80324
  }
@@ -80137,7 +80363,7 @@ function pendingDeviceSignInOrigins(env3 = process.env, now3 = Date.now()) {
80137
80363
  if (!entry.startsWith(`${location.stem}-`) || !/-[0-9a-f]{16}\.json$/.test(entry))
80138
80364
  continue;
80139
80365
  try {
80140
- const parsed = JSON.parse(readFileSync20(join36(location.dir, entry), "utf8"));
80366
+ const parsed = JSON.parse(readFileSync21(join36(location.dir, entry), "utf8"));
80141
80367
  if (isRecord6(parsed) && typeof parsed.origin === "string" && Number(parsed.expiresAt) > now3 && pendingDeviceSignInPath(parsed.origin, env3) === join36(location.dir, entry))
80142
80368
  origins.push(parsed.origin);
80143
80369
  } catch {}
@@ -80811,7 +81037,7 @@ var init_skill_bundles = __esm(() => {
80811
81037
  });
80812
81038
 
80813
81039
  // src/lib/pull.ts
80814
- import { existsSync as existsSync21, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync3, readFileSync as readFileSync21, renameSync as renameSync9, rmSync as rmSync6, writeFileSync as writeFileSync12 } from "fs";
81040
+ import { existsSync as existsSync21, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync3, readFileSync as readFileSync23, renameSync as renameSync9, rmSync as rmSync6, writeFileSync as writeFileSync12 } from "fs";
80815
81041
  import { dirname as dirname21, join as join37 } from "path";
80816
81042
  async function pullSkills(options = {}) {
80817
81043
  const client2 = options.client !== undefined ? options.client : await createRemoteSkillsClient();
@@ -80973,7 +81199,7 @@ function reconcileTombstone(slug2, corpusOptions) {
80973
81199
  }
80974
81200
  function readPullMarker(dir) {
80975
81201
  try {
80976
- return JSON.parse(readFileSync21(join37(dir, PULL_MARKER_FILE), "utf-8"));
81202
+ return JSON.parse(readFileSync23(join37(dir, PULL_MARKER_FILE), "utf-8"));
80977
81203
  } catch {
80978
81204
  return null;
80979
81205
  }
@@ -81343,6 +81569,7 @@ var init_skill_context = __esm(() => {
81343
81569
 
81344
81570
  // src/lib/session-reconciliation.ts
81345
81571
  import { createHash as createHash22 } from "crypto";
81572
+ import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
81346
81573
  function exactTimestamp(value) {
81347
81574
  const parsed = value === undefined ? Number.NaN : Date.parse(value);
81348
81575
  if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
@@ -81375,6 +81602,52 @@ function inspectSkillSession(sessionId, options = {}) {
81375
81602
  loadedCount: receipt.loaded.length
81376
81603
  };
81377
81604
  }
81605
+ async function reconcileSkillSessionIfSafe(sessionId, profileId, options = {}) {
81606
+ const snapshot2 = readSkillSessionSnapshotIfExists(sessionId, options);
81607
+ if (!snapshot2)
81608
+ return false;
81609
+ const old = snapshot2.receipt;
81610
+ if (old.profile.profileId !== profileId)
81611
+ throw new SkillSelectionError("PROFILE_LOCK_MISMATCH", "The session is pinned to a different Skills profile.");
81612
+ const now3 = (options.now ?? Date.now)();
81613
+ const age = now3 - Date.parse(old.verifiedAt);
81614
+ if (age >= 0 && age <= MAX_CACHED_PROFILE_AGE_MS)
81615
+ return false;
81616
+ if (age < 0)
81617
+ throw new SkillSelectionError("SESSION_RECONCILIATION_REQUIRED", "The session receipt has a future timestamp; inspect it before changing its pin.");
81618
+ const client2 = options.client ?? await createProfileClient();
81619
+ const target = structuredClone(await client2.resolveProfile(profileId));
81620
+ validateResolvedProfile(target, client2.authority);
81621
+ if (target.profileId !== profileId || target.authority !== old.profile.authority || target.workspaceId !== old.profile.workspaceId) {
81622
+ throw new SkillSelectionError("PROFILE_IDENTITY_MISMATCH", "The authenticated Skills profile does not match this session's profile, authority and workspace.");
81623
+ }
81624
+ const oldByKey = new Map(old.profile.selections.map((selection) => [selectionKey(selection), selection]));
81625
+ const targetBySlug = new Map(target.selections.map((selection) => [selection.slug, selection]));
81626
+ for (const key of old.loaded) {
81627
+ const previous = oldByKey.get(key);
81628
+ const current = targetBySlug.get(previous.slug);
81629
+ if (!current || !isDeepStrictEqual8({ ...previous, profileRevision: target.profileRevision }, current)) {
81630
+ throw new SkillSelectionError("SESSION_RECONCILIATION_REQUIRED", "A loaded Skills selection was removed or changed; review skills sessions reconcile before changing this session's pin.");
81631
+ }
81632
+ }
81633
+ const replacement = {
81634
+ ...old,
81635
+ generation: nextSkillSessionGeneration(snapshot2.generation),
81636
+ verifiedAt: new Date((options.now ?? Date.now)()).toISOString(),
81637
+ profile: target,
81638
+ loaded: [...old.loaded]
81639
+ };
81640
+ replaceSkillSession(skillSessionSnapshotBinding(snapshot2), replacement, {
81641
+ schemaVersion: 1,
81642
+ kind: "managed-hook-safe-reconciliation",
81643
+ sessionId,
81644
+ fromProfileRevision: old.profile.profileRevision,
81645
+ toProfileRevision: target.profileRevision,
81646
+ retainedLoadedCount: old.loaded.length,
81647
+ retiredLoadedCount: 0
81648
+ }, options);
81649
+ return true;
81650
+ }
81378
81651
  async function reconcileSkillSession(input2, options = {}) {
81379
81652
  if (![input2.fromProfile, input2.fromRevision, input2.selectionProfile, input2.profileRevision].every((value) => typeof value === "string" && value.trim()) || !/^[a-f0-9]{64}$/.test(input2.receiptSha256)) {
81380
81653
  throw new SkillSelectionError("INVALID_SESSION_RECONCILIATION", "Name the exact old receipt SHA256, old profile/revision and intended target profile/revision.");
@@ -81475,7 +81748,25 @@ Receipt SHA256: ${result2.receiptSha256}`);
81475
81748
  reportContextError(error61, options.json);
81476
81749
  }
81477
81750
  });
81478
- sessions.command("reconcile <id>").requiredOption("--from-profile <id>", "Expected current session profile").requiredOption("--from-revision <revision>", "Expected current session profile revision").requiredOption("--receipt-sha256 <sha256>", "Expected exact current receipt bytes").requiredOption("--selection-profile <id>", "Intended target selection profile").requiredOption("--profile-revision <revision>", "Expected current API target revision").option("--apply", "Archive the old receipt and atomically apply the reviewed plan", false).option("--plan-digest <sha256>", "Exact reviewed plan digest; required with --apply").option("--plan-issued-at <timestamp>", "Exact reviewed plan issuedAt; required with --apply").option("--plan-expires-at <timestamp>", "Exact reviewed plan expiresAt; required with --apply").option("--json", "Return the plan or application receipt as JSON", false).description("Plan an intentional migration of one session pin; ordinary hooks never migrate pins").action(async (id, options) => {
81751
+ sessions.command("recover-lock <id>").option("--apply", "Archive the exact reviewed stale lock", false).option("--review-digest <sha256>", "Exact digest returned by the lock review; required with --apply").option("--confirm-local-host", "Confirm this PID-only legacy lock was created on this host", false).option("--json", "Return lock metadata or recovery result as JSON", false).description("Review a stale session write lock, then archive it without changing the receipt").action(async (id, options) => {
81752
+ try {
81753
+ if (!options.apply) {
81754
+ const result2 = inspectSessionWriteLock(id);
81755
+ await writeCliOutput(options.json ? JSON.stringify(result2) : `Lock for ${JSON.stringify(id)}: PID ${result2.pid}, schema ${result2.schemaVersion}, modified ${result2.lockMtime}.
81756
+ Receipt SHA256: ${result2.receiptSha256}
81757
+ Review digest: ${result2.reviewDigest}`);
81758
+ return;
81759
+ }
81760
+ if (!options.reviewDigest || !/^[a-f0-9]{64}$/.test(options.reviewDigest) || !options.confirmLocalHost) {
81761
+ throw new SkillSelectionError("SESSION_LOCK_REVIEW_REQUIRED", "Apply requires the exact review digest and --confirm-local-host after verifying this lock belongs to this host.");
81762
+ }
81763
+ const archivePath = recoverSessionWriteLock(id, options.reviewDigest);
81764
+ await writeCliOutput(options.json ? JSON.stringify({ sessionId: id, recovered: true, archivePath }) : `Archived the stale lock at ${archivePath}. The session receipt was not changed.`);
81765
+ } catch (error61) {
81766
+ reportContextError(error61, options.json);
81767
+ }
81768
+ });
81769
+ sessions.command("reconcile <id>").requiredOption("--from-profile <id>", "Expected current session profile").requiredOption("--from-revision <revision>", "Expected current session profile revision").requiredOption("--receipt-sha256 <sha256>", "Expected exact current receipt bytes").requiredOption("--selection-profile <id>", "Intended target selection profile").requiredOption("--profile-revision <revision>", "Expected current API target revision").option("--apply", "Archive the old receipt and atomically apply the reviewed plan", false).option("--plan-digest <sha256>", "Exact reviewed plan digest; required with --apply").option("--plan-issued-at <timestamp>", "Exact reviewed plan issuedAt; required with --apply").option("--plan-expires-at <timestamp>", "Exact reviewed plan expiresAt; required with --apply").option("--json", "Return the plan or application receipt as JSON", false).description("Plan an intentional migration of one session pin when loaded selections changed").action(async (id, options) => {
81479
81770
  try {
81480
81771
  const result2 = await reconcileSkillSession({ ...options, sessionId: id });
81481
81772
  await writeCliOutput(options.json ? JSON.stringify(result2) : result2.applied ? `Reconciled ${JSON.stringify(id)} to ${result2.plan.target.profileId} at ${result2.plan.target.profileRevision}.
@@ -81497,18 +81788,27 @@ Preserved original receipt: ${result2.archivePath}` : `Planned one session recon
81497
81788
  reportContextError(error61, options.json);
81498
81789
  }
81499
81790
  });
81500
- parent.command("context [prompt]").description("Select bounded, versioned skill context for this prompt without running skills").option("--stdin", "Read native hook JSON or a prompt from stdin", false).option("--selection-profile <id>", "Selection profile (separate from the credential --profile)").option("--cached", "Use only an explicitly selected verified cache, at most 24 hours old", false).option("--session <id>", "Keep the same skill versions across this session").option("--restore", "Re-emit loaded session context after compaction or resume", false).option("--max-chars <count>", "Maximum injected characters", "8000").option("--max-skills <count>", "Maximum loaded skills", "3").option("--json", "Return context and selection receipts as JSON", false).action(async (prompt, options) => {
81791
+ parent.command("context [prompt]").description("Select bounded, versioned skill context for this prompt without running skills").option("--stdin", "Read native hook JSON or a prompt from stdin", false).option("--selection-profile <id>", "Selection profile (separate from the credential --profile)").option("--cached", "Use only an explicitly selected verified cache, at most 24 hours old", false).option("--auto-reconcile-safe", "For managed hooks, archive and renew an expired session only when all loaded selections are unchanged", false).option("--session <id>", "Keep the same skill versions across this session").option("--restore", "Re-emit loaded session context after compaction or resume", false).option("--max-chars <count>", "Maximum injected characters", "8000").option("--max-skills <count>", "Maximum loaded skills", "3").option("--json", "Return context and selection receipts as JSON", false).action(async (prompt, options) => {
81501
81792
  try {
81793
+ if (options.autoReconcileSafe && !options.cached)
81794
+ throw new SkillSelectionError("INVALID_CONTEXT_INPUT", "--auto-reconcile-safe requires --cached.");
81502
81795
  const input2 = options.stdin ? parseSkillContextInput(await readContextStdin()) : { prompt: prompt ?? "" };
81503
81796
  input2.profileId = selectedProfileId(options.selectionProfile ?? input2.profileId);
81504
81797
  input2.cwd ??= process.cwd();
81505
81798
  input2.sessionId = options.session ?? input2.sessionId;
81506
81799
  input2.restore ||= options.restore;
81507
- const result2 = await buildSkillContext(input2, {
81508
- ...contextResolverOptions(options),
81509
- maxChars: Number(options.maxChars),
81510
- maxSkills: Number(options.maxSkills)
81511
- });
81800
+ const resolverOptions = { ...contextResolverOptions(options), maxChars: Number(options.maxChars), maxSkills: Number(options.maxSkills) };
81801
+ let result2;
81802
+ try {
81803
+ result2 = await buildSkillContext(input2, resolverOptions);
81804
+ } catch (error61) {
81805
+ if (!options.cached || !options.autoReconcileSafe || !(error61 instanceof SkillSelectionError) || error61.code !== "CACHED_PROFILE_EXPIRED" || !input2.sessionId)
81806
+ throw error61;
81807
+ const sessionId = input2.agentId ? `${input2.sessionId}:${input2.agentId}` : input2.sessionId;
81808
+ if (!await reconcileSkillSessionIfSafe(sessionId, input2.profileId, resolverOptions))
81809
+ throw error61;
81810
+ result2 = await buildSkillContext(input2, resolverOptions);
81811
+ }
81512
81812
  await writeCliOutput(options.json ? JSON.stringify(result2) : result2.context);
81513
81813
  } catch (error61) {
81514
81814
  reportContextError(error61, options.json);
@@ -81592,13 +81892,13 @@ var init_context = __esm(() => {
81592
81892
  });
81593
81893
 
81594
81894
  // src/cli/commands/profile-sync.ts
81595
- import { hostname as hostname3 } from "os";
81895
+ import { hostname as hostname4 } from "os";
81596
81896
  async function handleProfileSync(options, dependencies = {}) {
81597
81897
  try {
81598
81898
  const result2 = await syncSelectionProfile(selectedProfileId(options.selectionProfile ?? options.profile), {
81599
81899
  ...dependencies,
81600
81900
  projectDir: options.project ? process.cwd() : dependencies.projectDir,
81601
- stationId: options.stationReport === false ? undefined : options.station ?? dependencies.stationId ?? process.env.HASNA_STATION ?? hostname3(),
81901
+ stationId: options.stationReport === false ? undefined : options.station ?? dependencies.stationId ?? process.env.HASNA_STATION ?? hostname4(),
81602
81902
  check: options.check ?? dependencies.check
81603
81903
  });
81604
81904
  if (options.json)
@@ -82326,7 +82626,7 @@ var exports_introspect = {};
82326
82626
  __export(exports_introspect, {
82327
82627
  registerIntrospect: () => registerIntrospect
82328
82628
  });
82329
- import { existsSync as existsSync22, readFileSync as readFileSync23 } from "fs";
82629
+ import { existsSync as existsSync22, readFileSync as readFileSync25 } from "fs";
82330
82630
  import { join as join38 } from "path";
82331
82631
  import { execSync } from "child_process";
82332
82632
  function registerIntrospect(parent) {
@@ -82548,7 +82848,7 @@ function handleDiff(name, options) {
82548
82848
  present: existsSync22(canonicalSkillMd),
82549
82849
  path: canonicalDir,
82550
82850
  ...existsSync22(canonicalSkillMd) ? { hash: hashSkillMarkdownFile(canonicalSkillMd) } : {},
82551
- ...existsSync22(canonicalSkillMd) ? { stub: isPointerSkillMd(readFileSync23(canonicalSkillMd, "utf-8")) } : {}
82851
+ ...existsSync22(canonicalSkillMd) ? { stub: isPointerSkillMd(readFileSync25(canonicalSkillMd, "utf-8")) } : {}
82552
82852
  };
82553
82853
  const pinned = getInstalledSkills().includes(bare);
82554
82854
  const installMeta = getInstallMeta();
@@ -82557,7 +82857,7 @@ function handleDiff(name, options) {
82557
82857
  let registryVersion = "unknown";
82558
82858
  if (existsSync22(registryPkgPath)) {
82559
82859
  try {
82560
- registryVersion = JSON.parse(readFileSync23(registryPkgPath, "utf-8")).version || "unknown";
82860
+ registryVersion = JSON.parse(readFileSync25(registryPkgPath, "utf-8")).version || "unknown";
82561
82861
  } catch {}
82562
82862
  }
82563
82863
  const upToDate = installedVersion === registryVersion;
@@ -82571,7 +82871,7 @@ function handleDiff(name, options) {
82571
82871
  let stub;
82572
82872
  if (present && existsSync22(skillMdPath)) {
82573
82873
  try {
82574
- stub = isPointerSkillMd(readFileSync23(skillMdPath, "utf-8"));
82874
+ stub = isPointerSkillMd(readFileSync25(skillMdPath, "utf-8"));
82575
82875
  } catch {
82576
82876
  stub = undefined;
82577
82877
  }
@@ -83109,7 +83409,7 @@ var exports_init = {};
83109
83409
  __export(exports_init, {
83110
83410
  registerSetup: () => registerSetup
83111
83411
  });
83112
- import { existsSync as existsSync23, readFileSync as readFileSync25, writeFileSync as writeFileSync13, appendFileSync } from "fs";
83412
+ import { existsSync as existsSync23, readFileSync as readFileSync26, writeFileSync as writeFileSync13, appendFileSync } from "fs";
83113
83413
  import { join as join39 } from "path";
83114
83414
  function registerSetup(parent) {
83115
83415
  parent.command("init").option("--json", "Output as JSON", false).option("--for <agent>", "Detect project type and show MCP registration guidance for agent").option("--scope <scope>", "Deprecated; agent skill-folder installs are disabled", "global").description("Initialize project for pinned skills (.env.example, .gitignore)").action((options) => handleInit(options));
@@ -83204,7 +83504,7 @@ Use: skills render`));
83204
83504
  console.log(source_default.dim(" No environment variables detected across pinned skills"));
83205
83505
  const gitignorePath = join39(cwd2, ".gitignore");
83206
83506
  const gitignoreEntries = [".skills/runs/", ".skills/exports/", ".skills/tmp/"];
83207
- let gitignoreContent = existsSync23(gitignorePath) ? readFileSync25(gitignorePath, "utf-8") : "";
83507
+ let gitignoreContent = existsSync23(gitignorePath) ? readFileSync26(gitignorePath, "utf-8") : "";
83208
83508
  let gitignoreUpdated = false;
83209
83509
  const missingEntries = gitignoreEntries.filter((entry) => !gitignoreContent.includes(entry));
83210
83510
  if (missingEntries.length > 0) {
@@ -83259,7 +83559,7 @@ async function handleImport(file2, options) {
83259
83559
  process.exitCode = 1;
83260
83560
  return;
83261
83561
  }
83262
- raw = readFileSync25(file2, "utf-8");
83562
+ raw = readFileSync26(file2, "utf-8");
83263
83563
  }
83264
83564
  } catch (err) {
83265
83565
  const error61 = `Failed to read file: ${err.message}`;
@@ -83356,7 +83656,7 @@ var init_init = __esm(() => {
83356
83656
  });
83357
83657
 
83358
83658
  // src/lib/home-adoption.ts
83359
- import { constants as constants13, closeSync as closeSync15, fstatSync as fstatSync15, lstatSync as lstatSync22, openSync as openSync15, readSync as readSync12, existsSync as existsSync24, mkdirSync as mkdirSync14, readdirSync as readdirSync14, readFileSync as readFileSync26, rmSync as rmSync7, statSync as statSync12, writeFileSync as writeFileSync14 } from "fs";
83659
+ import { constants as constants13, closeSync as closeSync15, fstatSync as fstatSync15, lstatSync as lstatSync22, openSync as openSync15, readSync as readSync12, existsSync as existsSync24, mkdirSync as mkdirSync14, readdirSync as readdirSync14, readFileSync as readFileSync27, rmSync as rmSync7, statSync as statSync12, writeFileSync as writeFileSync14 } from "fs";
83360
83660
  import { homedir as homedir8 } from "os";
83361
83661
  import { join as join40 } from "path";
83362
83662
  function indexCanonicalCorpus(corpusRoot) {
@@ -83460,7 +83760,7 @@ function appendConflictsLedger(appDir, conflicts) {
83460
83760
  let ledger = { version: 1, entries: [] };
83461
83761
  if (existsSync24(ledgerPath)) {
83462
83762
  try {
83463
- const parsed = JSON.parse(readFileSync26(ledgerPath, "utf-8"));
83763
+ const parsed = JSON.parse(readFileSync27(ledgerPath, "utf-8"));
83464
83764
  if (parsed && typeof parsed === "object" && Array.isArray(parsed.entries)) {
83465
83765
  ledger = parsed;
83466
83766
  }
@@ -83609,7 +83909,7 @@ var init_home_adoption = __esm(() => {
83609
83909
  });
83610
83910
 
83611
83911
  // src/lib/home-census.ts
83612
- import { existsSync as existsSync25, readFileSync as readFileSync27, readdirSync as readdirSync15, statSync as statSync13 } from "fs";
83912
+ import { existsSync as existsSync25, readFileSync as readFileSync28, readdirSync as readdirSync15, statSync as statSync13 } from "fs";
83613
83913
  import { homedir as homedir9 } from "os";
83614
83914
  import { join as join41 } from "path";
83615
83915
  function sortEntries(entries) {
@@ -83680,14 +83980,14 @@ function censusHomeDrift(options = {}) {
83680
83980
  if (homeHash !== canonicalHash) {
83681
83981
  let homeStub;
83682
83982
  try {
83683
- homeStub = isPointerSkillMd(readFileSync27(skillMdPath, "utf-8"));
83983
+ homeStub = isPointerSkillMd(readFileSync28(skillMdPath, "utf-8"));
83684
83984
  } catch {
83685
83985
  homeStub = undefined;
83686
83986
  }
83687
83987
  let canonicalStub;
83688
83988
  const canonicalSkillMd = join41(corpusRoot, skill, "SKILL.md");
83689
83989
  try {
83690
- canonicalStub = isPointerSkillMd(readFileSync27(canonicalSkillMd, "utf-8"));
83990
+ canonicalStub = isPointerSkillMd(readFileSync28(canonicalSkillMd, "utf-8"));
83691
83991
  } catch {
83692
83992
  canonicalStub = undefined;
83693
83993
  }
@@ -83725,7 +84025,7 @@ var init_home_census = __esm(() => {
83725
84025
  });
83726
84026
 
83727
84027
  // src/cli/env-assignment.ts
83728
- import { closeSync as closeSync16, constants as constants14, fstatSync as fstatSync16, ftruncateSync as ftruncateSync2, lstatSync as lstatSync23, openSync as openSync16, readFileSync as readFileSync28, writeSync as writeSync3 } from "fs";
84028
+ import { closeSync as closeSync16, constants as constants14, fstatSync as fstatSync16, ftruncateSync as ftruncateSync2, lstatSync as lstatSync23, openSync as openSync16, readFileSync as readFileSync29, writeSync as writeSync3 } from "fs";
83729
84029
  function serializeValue(value) {
83730
84030
  if (controls.test(value))
83731
84031
  throw new EnvAssignmentError("Environment values cannot contain control characters or newlines.");
@@ -83820,7 +84120,7 @@ function setEnvAssignment(path, assignment) {
83820
84120
  }
83821
84121
  let prepared = initial2;
83822
84122
  if (previous) {
83823
- const original = readFileSync28(descriptor), text4 = original.toString("utf8");
84123
+ const original = readFileSync29(descriptor), text4 = original.toString("utf8");
83824
84124
  if (!Buffer.from(text4, "utf8").equals(original))
83825
84125
  throw new EnvAssignmentError(INVALID_LAYOUT);
83826
84126
  prepared = prepareEnvAssignment(assignment, text4);
@@ -83907,7 +84207,7 @@ var exports_diagnostic = {};
83907
84207
  __export(exports_diagnostic, {
83908
84208
  registerDiagnostic: () => registerDiagnostic
83909
84209
  });
83910
- import { existsSync as existsSync26, readFileSync as readFileSync29, readdirSync as readdirSync16, statSync as statSync14 } from "fs";
84210
+ import { existsSync as existsSync26, readFileSync as readFileSync30, readdirSync as readdirSync16, statSync as statSync14 } from "fs";
83911
84211
  import { join as join42 } from "path";
83912
84212
  import { execSync as execSync2 } from "child_process";
83913
84213
  function registerDiagnostic(parent) {
@@ -84148,7 +84448,7 @@ function handleOutdated(options) {
84148
84448
  let registryVersion = "unknown";
84149
84449
  if (existsSync26(registryPkgPath))
84150
84450
  try {
84151
- registryVersion = JSON.parse(readFileSync29(registryPkgPath, "utf-8")).version || "unknown";
84451
+ registryVersion = JSON.parse(readFileSync30(registryPkgPath, "utf-8")).version || "unknown";
84152
84452
  } catch {}
84153
84453
  if (installedVersion !== registryVersion)
84154
84454
  pins.push({ skill: name, installedVersion, registryVersion });
@@ -84358,8 +84658,8 @@ var init_cloud_executions = __esm(() => {
84358
84658
 
84359
84659
  // node_modules/@hasna/secrets/dist/sdk.js
84360
84660
  import { isIP as isIP3 } from "net";
84361
- import { spawnSync as spawnSync2 } from "child_process";
84362
- import { closeSync as closeSync17, fstatSync as fstatSync17, openSync as openSync17, readFileSync as readFileSync30 } from "fs";
84661
+ import { spawnSync as spawnSync3 } from "child_process";
84662
+ import { closeSync as closeSync17, fstatSync as fstatSync17, openSync as openSync17, readFileSync as readFileSync31 } from "fs";
84363
84663
  import { O_NOFOLLOW as O_NOFOLLOW3, O_NONBLOCK as O_NONBLOCK3, O_RDONLY as O_RDONLY3 } from "constants";
84364
84664
  import { createRequire as createRequire2 } from "module";
84365
84665
  import { hostname as osHostname2 } from "os";
@@ -84485,7 +84785,7 @@ function readAppConfigFile2(path) {
84485
84785
  unsafe("the file is not owned by the current user");
84486
84786
  if (before2.size > MAX_CREDENTIAL_FILE_BYTES2)
84487
84787
  unsafe("the file exceeds the size limit");
84488
- const bytes = readFileSync30(fd);
84788
+ const bytes = readFileSync31(fd);
84489
84789
  const after2 = fstatSync17(fd);
84490
84790
  if (!configFileReadsCoherent2(before2, after2)) {
84491
84791
  unsafe("the file changed while being read");
@@ -84633,7 +84933,7 @@ function isAmbientEnvironment2(env3) {
84633
84933
  return env3 === process.env || env3[AMBIENT_ENVIRONMENT2] === true;
84634
84934
  }
84635
84935
  function defaultKeychainRunner2(argv) {
84636
- const result2 = spawnSync2(KEYCHAIN_SECURITY_BIN2, [...argv], {
84936
+ const result2 = spawnSync3(KEYCHAIN_SECURITY_BIN2, [...argv], {
84637
84937
  encoding: "utf8",
84638
84938
  stdio: ["ignore", "pipe", "pipe"],
84639
84939
  timeout: KEYCHAIN_SPAWN_TIMEOUT_MS2
@@ -85926,7 +86226,7 @@ var init_execution_grants = __esm(() => {
85926
86226
  });
85927
86227
 
85928
86228
  // src/lib/execution-grant-client.ts
85929
- import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
86229
+ import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
85930
86230
  function id(value) {
85931
86231
  if (!grantIdentifier(value))
85932
86232
  throw new Error("Invalid execution grant profile or revision identifier.");
@@ -85978,7 +86278,7 @@ async function saveExecutionGrantPolicy(profileId, grants, revision) {
85978
86278
  headers: revision === undefined ? { "If-None-Match": "*" } : { "If-Match": `"${revision}"` },
85979
86279
  body: JSON.stringify({ grants: snapshot2 })
85980
86280
  }));
85981
- if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual8(policy.grants, snapshot2)) {
86281
+ if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual9(policy.grants, snapshot2)) {
85982
86282
  throw new Error("The API did not preserve the reviewed execution grant policy; read it before retrying.");
85983
86283
  }
85984
86284
  return policy;
@@ -85989,7 +86289,7 @@ async function resolveExecutionGrant(input2) {
85989
86289
  if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
85990
86290
  throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
85991
86291
  }
85992
- if (!isDeepStrictEqual8(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual8(value.bindings.consumer, snapshot2.consumer)) {
86292
+ if (!isDeepStrictEqual9(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual9(value.bindings.consumer, snapshot2.consumer)) {
85993
86293
  throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
85994
86294
  }
85995
86295
  return value;
@@ -90352,7 +90652,7 @@ var init_runs = __esm(() => {
90352
90652
 
90353
90653
  // src/lib/run-state.ts
90354
90654
  import { createHash as createHash24, randomBytes } from "crypto";
90355
- import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync31, readdirSync as readdirSync17, statSync as statSync15, writeFileSync as writeFileSync16 } from "fs";
90655
+ import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync32, readdirSync as readdirSync17, statSync as statSync15, writeFileSync as writeFileSync16 } from "fs";
90356
90656
  import { extname, join as join45, relative as relative6 } from "path";
90357
90657
  function createSkillRun(params, targetDir = process.cwd()) {
90358
90658
  const now3 = new Date;
@@ -90424,7 +90724,7 @@ function appendRunEvent(context, event, data = {}) {
90424
90724
  const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
90425
90725
  `;
90426
90726
  const path = join45(context.runDir, "events.ndjson");
90427
- const previous = existsSync27(path) ? readFileSync31(path, "utf-8") : "";
90727
+ const previous = existsSync27(path) ? readFileSync32(path, "utf-8") : "";
90428
90728
  writeFileSync16(path, previous + line);
90429
90729
  }
90430
90730
  function listSkillRuns(targetDir = process.cwd(), limit = 50) {
@@ -90484,7 +90784,7 @@ function collectRunArtifacts(context) {
90484
90784
  const artifacts = [];
90485
90785
  for (const path of walkFiles(context.exportDir)) {
90486
90786
  const stat2 = statSync15(path);
90487
- const bytes = readFileSync31(path);
90787
+ const bytes = readFileSync32(path);
90488
90788
  artifacts.push({
90489
90789
  path: toProjectRelative(context.targetDir, path),
90490
90790
  mime: mimeForPath(path),
@@ -90499,7 +90799,7 @@ function readRunRecord(runDir) {
90499
90799
  if (!existsSync27(path))
90500
90800
  return null;
90501
90801
  try {
90502
- return JSON.parse(readFileSync31(path, "utf-8"));
90802
+ return JSON.parse(readFileSync32(path, "utf-8"));
90503
90803
  } catch {
90504
90804
  return null;
90505
90805
  }
@@ -102524,7 +102824,7 @@ var init_remote_customer_operations = __esm(() => {
102524
102824
  });
102525
102825
 
102526
102826
  // src/lib/workspace-profile.ts
102527
- import { constants as constants16, closeSync as closeSync19, fstatSync as fstatSync19, lstatSync as lstatSync24, mkdirSync as mkdirSync17, mkdtempSync as mkdtempSync5, openSync as openSync19, readFileSync as readFileSync32, renameSync as renameSync10, rmSync as rmSync8, writeFileSync as writeFileSync17 } from "fs";
102827
+ import { constants as constants16, closeSync as closeSync19, fstatSync as fstatSync19, lstatSync as lstatSync24, mkdirSync as mkdirSync17, mkdtempSync as mkdtempSync5, openSync as openSync19, readFileSync as readFileSync33, renameSync as renameSync10, rmSync as rmSync8, writeFileSync as writeFileSync17 } from "fs";
102528
102828
  import { dirname as dirname23, join as join46 } from "path";
102529
102829
  function stat2(path) {
102530
102830
  try {
@@ -102543,7 +102843,7 @@ function safeText(file2) {
102543
102843
  const s = fstatSync19(fd);
102544
102844
  if (!s.isFile() || s.size > 65536 || ![256, 384].includes(s.mode & 4095) || process.getuid && s.uid !== process.getuid())
102545
102845
  return fail3("The selected profile must use bounded owner-only regular files.");
102546
- return readFileSync32(fd, "utf8");
102846
+ return readFileSync33(fd, "utf8");
102547
102847
  } finally {
102548
102848
  closeSync19(fd);
102549
102849
  }
@@ -105421,7 +105721,7 @@ var init_resource_meta_tools = __esm(() => {
105421
105721
  });
105422
105722
 
105423
105723
  // src/lib/scheduler.ts
105424
- import { existsSync as existsSync30, readFileSync as readFileSync33, writeFileSync as writeFileSync19, mkdirSync as mkdirSync20 } from "fs";
105724
+ import { existsSync as existsSync30, readFileSync as readFileSync34, writeFileSync as writeFileSync19, mkdirSync as mkdirSync20 } from "fs";
105425
105725
  import { join as join50 } from "path";
105426
105726
  function getSchedulesPath(targetDir = process.cwd()) {
105427
105727
  return join50(targetDir, ".skills", "schedules.json");
@@ -105430,7 +105730,7 @@ function loadSchedules(targetDir = process.cwd()) {
105430
105730
  const path = getSchedulesPath(targetDir);
105431
105731
  if (existsSync30(path)) {
105432
105732
  try {
105433
- return JSON.parse(readFileSync33(path, "utf-8"));
105733
+ return JSON.parse(readFileSync34(path, "utf-8"));
105434
105734
  } catch {}
105435
105735
  }
105436
105736
  return { version: 1, schedules: [] };
@@ -105740,7 +106040,7 @@ import { createHash as createHash25, createHmac as createHmac3 } from "crypto";
105740
106040
  import {
105741
106041
  existsSync as existsSync31,
105742
106042
  mkdirSync as mkdirSync21,
105743
- readFileSync as readFileSync34,
106043
+ readFileSync as readFileSync35,
105744
106044
  readdirSync as readdirSync19,
105745
106045
  statSync as statSync17,
105746
106046
  writeFileSync as writeFileSync20
@@ -105808,7 +106108,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
105808
106108
  const files = [];
105809
106109
  if (existsSync31(projectStateDir)) {
105810
106110
  for (const filePath of walkFiles2(projectStateDir)) {
105811
- const bytes = readFileSync34(filePath);
106111
+ const bytes = readFileSync35(filePath);
105812
106112
  const relativePath = toPosix(relative7(targetDir, filePath));
105813
106113
  files.push({
105814
106114
  path: relativePath,
@@ -106713,7 +107013,7 @@ var exports_runtime_mcp = {};
106713
107013
  __export(exports_runtime_mcp, {
106714
107014
  handleMcp: () => handleMcp
106715
107015
  });
106716
- import { existsSync as existsSync32, mkdirSync as mkdirSync23, readFileSync as readFileSync35, writeFileSync as writeFileSync22 } from "fs";
107016
+ import { existsSync as existsSync32, mkdirSync as mkdirSync23, readFileSync as readFileSync36, writeFileSync as writeFileSync22 } from "fs";
106717
107017
  import { homedir as homedir10 } from "os";
106718
107018
  import { dirname as dirname28, join as join53 } from "path";
106719
107019
  async function handleMcp(options) {
@@ -106824,7 +107124,7 @@ function registerCodexMcp(command) {
106824
107124
  const config2 = `[mcp_servers.${MCP_SERVER_NAME}]
106825
107125
  command = ${JSON.stringify(command)}`;
106826
107126
  try {
106827
- const current = existsSync32(path) ? readFileSync35(path, "utf-8") : "";
107127
+ const current = existsSync32(path) ? readFileSync36(path, "utf-8") : "";
106828
107128
  writeTextFile(path, upsertTomlSection(current, `[mcp_servers.${MCP_SERVER_NAME}]`, `command = ${JSON.stringify(command)}`));
106829
107129
  return { agent: "codex", success: true, path, config: config2 };
106830
107130
  } catch (err) {
@@ -106876,7 +107176,7 @@ function registerJsonMcpServer(agent, path, containerKey, server2) {
106876
107176
  function readJsonObject2(path) {
106877
107177
  if (!existsSync32(path))
106878
107178
  return {};
106879
- const raw = readFileSync35(path, "utf-8").trim();
107179
+ const raw = readFileSync36(path, "utf-8").trim();
106880
107180
  if (!raw)
106881
107181
  return {};
106882
107182
  const parsed = JSON.parse(raw);
@@ -107000,7 +107300,7 @@ __export(exports_runtime, {
107000
107300
  registerRuntime: () => registerRuntime
107001
107301
  });
107002
107302
  import { createHash as createHash26, randomUUID as randomUUID9 } from "crypto";
107003
- import { lstatSync as lstatSync27, mkdirSync as mkdirSync24, readFileSync as readFileSync36, realpathSync as realpathSync11, writeFileSync as writeFileSync23 } from "fs";
107303
+ import { lstatSync as lstatSync27, mkdirSync as mkdirSync24, readFileSync as readFileSync37, realpathSync as realpathSync11, writeFileSync as writeFileSync23 } from "fs";
107004
107304
  import { basename as basename13, isAbsolute as isAbsolute22, join as join54 } from "path";
107005
107305
  import { createInterface } from "readline";
107006
107306
  function registerRuntime(parent) {
@@ -107453,7 +107753,7 @@ async function handleRun(name, args2, options, separatorBeforeSkill) {
107453
107753
  const info = lstatSync27(path);
107454
107754
  if (!info.isFile() || info.size > 20 * 1024 * 1024)
107455
107755
  throw new Error("Input must be a regular file no larger than 20 MiB");
107456
- return { name: basename13(path), bytes: new Uint8Array(readFileSync36(path)) };
107756
+ return { name: basename13(path), bytes: new Uint8Array(readFileSync37(path)) };
107457
107757
  });
107458
107758
  describeRemoteFiles(inputFiles);
107459
107759
  client2 = new RemoteSkillsClient(routing.apiKey, routing.apiOrigin);
@@ -108625,7 +108925,7 @@ import { createHash as createHash27 } from "crypto";
108625
108925
  import {
108626
108926
  copyFileSync,
108627
108927
  mkdirSync as mkdirSync25,
108628
- readFileSync as readFileSync37,
108928
+ readFileSync as readFileSync38,
108629
108929
  statSync as statSync19,
108630
108930
  writeFileSync as writeFileSync24
108631
108931
  } from "fs";
@@ -108636,7 +108936,7 @@ function validateStationId(stationId) {
108636
108936
  }
108637
108937
  }
108638
108938
  function sha256File(filePath) {
108639
- return createHash27("sha256").update(readFileSync37(filePath)).digest("hex");
108939
+ return createHash27("sha256").update(readFileSync38(filePath)).digest("hex");
108640
108940
  }
108641
108941
  function scanHome(definition, homesRoot) {
108642
108942
  const homePath = homePathFor(definition, homesRoot);
@@ -109208,7 +109508,7 @@ var init_create_sync_config = __esm(() => {
109208
109508
  });
109209
109509
 
109210
109510
  // src/lib/codex-native-config.ts
109211
- import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
109511
+ import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
109212
109512
  function isRecord7(value) {
109213
109513
  if (!value || typeof value !== "object")
109214
109514
  return false;
@@ -109230,7 +109530,7 @@ function decimalIdentity(text4) {
109230
109530
  return `${match[1]}${coefficient}e${exponent}`;
109231
109531
  }
109232
109532
  function codexNativeConfigEqual(native, expected) {
109233
- if (isDeepStrictEqual9(native, expected))
109533
+ if (isDeepStrictEqual10(native, expected))
109234
109534
  return true;
109235
109535
  if (typeof expected === "number") {
109236
109536
  if (!Number.isFinite(expected) || !isRecord7(native))
@@ -109396,7 +109696,7 @@ var init_codex_hook_rpc = __esm(() => {
109396
109696
  // src/lib/codex-hook-trust-identity.ts
109397
109697
  import { realpathSync as realpathSync13 } from "fs";
109398
109698
  import { dirname as dirname31, isAbsolute as isAbsolute25, join as join57 } from "path";
109399
- import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
109699
+ import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
109400
109700
  function inspectRecordedSkillsCli(command, expected) {
109401
109701
  need4(isAbsolute25(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
109402
109702
  const resolved = realpathSync13(command);
@@ -109406,7 +109706,7 @@ function inspectRecordedSkillsCli(command, expected) {
109406
109706
  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");
109407
109707
  need4((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
109408
109708
  const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
109409
- need4(isDeepStrictEqual10(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
109709
+ need4(isDeepStrictEqual11(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
109410
109710
  return { receipt, recheck() {
109411
109711
  need4(realpathSync13(command) === resolved, "SKILLS_COMMAND_CHANGED");
109412
109712
  unchanged2(cli);
@@ -109442,19 +109742,19 @@ var init_codex_hook_trust_identity = __esm(() => {
109442
109742
  });
109443
109743
 
109444
109744
  // src/lib/codex-hook-policy-recovery.ts
109445
- import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
109745
+ import { isDeepStrictEqual as isDeepStrictEqual12 } from "util";
109446
109746
  function reviewedClaudeDiscoveryChange(beforeText, currentText, options) {
109447
109747
  need4(typeof options.reason === "string" && options.reason.trim().length > 0 && options.reason.length <= 512 && !/[\x00-\x1f\x7f]/.test(options.reason), "RECONCILE_RECOVERY_REASON_REQUIRED");
109448
109748
  need4(options.reviewedPlanDigest === undefined || /^[a-f0-9]{64}$/.test(options.reviewedPlanDigest), "RECONCILE_RECOVERY_PLAN_CHANGED");
109449
109749
  const before2 = parseManagedSkillPolicy(beforeText), current = parseManagedSkillPolicy(currentText);
109450
109750
  const oldDiscovery = before2.bridge?.discovery?.claude, newDiscovery = current.bridge?.discovery?.claude;
109451
109751
  need4(oldDiscovery?.agent === "claude" && newDiscovery?.agent === "claude" && oldDiscovery.method === "reviewed" && newDiscovery.method === "reviewed", "RECONCILE_POLICY_CHANGED");
109452
- const changes = ["roots", "sources"].filter((key) => !isDeepStrictEqual11(oldDiscovery[key], newDiscovery[key])).map((key) => ({ path: `bridge.discovery.claude.${key}`, before: oldDiscovery[key], after: newDiscovery[key] }));
109752
+ const changes = ["roots", "sources"].filter((key) => !isDeepStrictEqual12(oldDiscovery[key], newDiscovery[key])).map((key) => ({ path: `bridge.discovery.claude.${key}`, before: oldDiscovery[key], after: newDiscovery[key] }));
109453
109753
  need4(changes.length > 0, "RECONCILE_POLICY_CHANGED");
109454
109754
  const compared = structuredClone(current);
109455
109755
  compared.bridge.discovery.claude.roots = oldDiscovery.roots;
109456
109756
  compared.bridge.discovery.claude.sources = oldDiscovery.sources;
109457
- need4(isDeepStrictEqual11(before2, compared), "RECONCILE_POLICY_CHANGED");
109757
+ need4(isDeepStrictEqual12(before2, compared), "RECONCILE_POLICY_CHANGED");
109458
109758
  verifyAgentDiscovery(newDiscovery);
109459
109759
  return { reason: options.reason, changes };
109460
109760
  }
@@ -109465,23 +109765,23 @@ var init_codex_hook_policy_recovery = __esm(() => {
109465
109765
  });
109466
109766
 
109467
109767
  // src/lib/agent-codex-trust.ts
109468
- import { lstatSync as lstatSync29, mkdirSync as mkdirSync26, readdirSync as readdirSync21, readFileSync as readFileSync38, realpathSync as realpathSync14, statSync as statSync21 } from "fs";
109768
+ import { lstatSync as lstatSync29, mkdirSync as mkdirSync26, readdirSync as readdirSync21, readFileSync as readFileSync39, realpathSync as realpathSync14, statSync as statSync21 } from "fs";
109469
109769
  import { createHash as createHash28, randomUUID as randomUUID10 } from "crypto";
109470
109770
  import { execFileSync } from "child_process";
109471
109771
  import { dirname as dirname32, isAbsolute as isAbsolute26, join as join58, resolve as resolve23 } from "path";
109472
109772
  import { homedir as homedir12 } from "os";
109473
- import { isDeepStrictEqual as isDeepStrictEqual12 } from "util";
109773
+ import { isDeepStrictEqual as isDeepStrictEqual13 } from "util";
109474
109774
  function verifyReleaseArtifact(proof, skillsCli) {
109475
109775
  need4(isAbsolute26(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
109476
109776
  const tarPath = realpathSync14(proof.packageTarPath);
109477
109777
  need4(statSync21(tarPath).isFile(), "RECONCILE_RELEASE_PROVENANCE_PATH");
109478
- const tarBytes = readFileSync38(tarPath);
109778
+ const tarBytes = readFileSync39(tarPath);
109479
109779
  need4(tarBytes.byteLength === proof.packageTarBytes && createHash28("sha256").update(tarBytes).digest("hex") === proof.packageTarSha256, "RECONCILE_RELEASE_ARTIFACT_CHANGED");
109480
109780
  const packageRoot = dirname32(dirname32(realpathSync14(skillsCli.receipt.path)));
109481
- const manifest = readFileSync38(join58(packageRoot, "package.json"));
109781
+ const manifest = readFileSync39(join58(packageRoot, "package.json"));
109482
109782
  need4(createHash28("sha256").update(manifest).digest("hex") === skillsCli.receipt.manifestSha256 && skillsCli.receipt.manifestSha256 === proof.manifestSha256, "RECONCILE_RELEASE_MANIFEST_CHANGED");
109483
- need4(createHash28("sha256").update(readFileSync38(skillsCli.receipt.path)).digest("hex") === proof.executableSha256 && skillsCli.receipt.sha256 === proof.executableSha256, "RECONCILE_RELEASE_EXECUTABLE_CHANGED");
109484
- const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync38(skillsCli.receipt.path)]];
109783
+ need4(createHash28("sha256").update(readFileSync39(skillsCli.receipt.path)).digest("hex") === proof.executableSha256 && skillsCli.receipt.sha256 === proof.executableSha256, "RECONCILE_RELEASE_EXECUTABLE_CHANGED");
109784
+ const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync39(skillsCli.receipt.path)]];
109485
109785
  for (const [member, expected] of members2) {
109486
109786
  let actual;
109487
109787
  try {
@@ -109512,7 +109812,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109512
109812
  const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
109513
109813
  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 };
109514
109814
  need4(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
109515
- need4(isDeepStrictEqual12(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
109815
+ need4(isDeepStrictEqual13(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
109516
109816
  need4(configBefore.sha256 === (intent.beforeSha256 ?? intent.configSha256) && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
109517
109817
  const policyCurrent = snapshot(join58(dataDir3, "agent-policy.json"), true);
109518
109818
  const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
@@ -109525,7 +109825,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109525
109825
  if (policyCurrent.sha256 !== policyBefore.sha256 && !claudeRecovery) {
109526
109826
  need4(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
109527
109827
  const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
109528
- need4(isDeepStrictEqual12(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
109828
+ need4(isDeepStrictEqual13(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
109529
109829
  supersededBinding = true;
109530
109830
  }
109531
109831
  if (supersededBinding || recovery) {
@@ -109561,7 +109861,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109561
109861
  need4(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
109562
109862
  }
109563
109863
  } else
109564
- need4(isDeepStrictEqual12(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
109864
+ need4(isDeepStrictEqual13(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
109565
109865
  const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig, intent.hooks, additions?.trustProof) : undefined;
109566
109866
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: configDiscoveryRecovery });
109567
109867
  const recoveryCli = recovery ? bindSkillsCli("skills", options.reviewedSkillsCli) : undefined;
@@ -109570,14 +109870,14 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109570
109870
  const skillsCli = recovery ? inspectRecordedSkillsCli(binding.commands.codex, intent.skillsCli) : bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
109571
109871
  if (supersededBinding)
109572
109872
  verifyReleaseArtifact(options.releaseProof, skillsCli);
109573
- const skillsBindingChanged = !isDeepStrictEqual12(skillsCli.receipt, intent.skillsCli);
109873
+ const skillsBindingChanged = !isDeepStrictEqual13(skillsCli.receipt, intent.skillsCli);
109574
109874
  need4(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
109575
109875
  if (!additions && hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
109576
109876
  need4(supersededBinding, "RECONCILE_HOOKS_CHANGED");
109577
109877
  const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
109578
109878
  for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
109579
109879
  const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
109580
- need4(isDeepStrictEqual12(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
109880
+ need4(isDeepStrictEqual13(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
109581
109881
  }
109582
109882
  }
109583
109883
  rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
@@ -109600,9 +109900,9 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109600
109900
  delete identity4.command;
109601
109901
  return identity4;
109602
109902
  };
109603
- need4(isDeepStrictEqual12(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
109903
+ need4(isDeepStrictEqual13(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
109604
109904
  if (!plannedKeys.has(expected.key))
109605
- need4(isDeepStrictEqual12(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
109905
+ need4(isDeepStrictEqual13(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
109606
109906
  }
109607
109907
  for (const wanted of intent.hooks) {
109608
109908
  const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join58(home, ".codex/hooks.json"), hooksPath));
@@ -109621,7 +109921,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
109621
109921
  const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
109622
109922
  need4(layers?.length === 1 && !layers[0].disabledReason && codexNativeConfigEqual(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
109623
109923
  const finalDiscovery = await rpc.request("hooks/list", { cwds: [home] });
109624
- need4(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks || isDeepStrictEqual12(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
109924
+ need4(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks || isDeepStrictEqual13(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
109625
109925
  const finalConfig = await rpc.request("config/read", { includeLayers: true, cwd: home });
109626
109926
  const finalLayers = finalConfig?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
109627
109927
  need4(finalLayers?.length === 1 && !finalLayers[0].disabledReason && codexNativeConfigEqual(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
@@ -109682,7 +109982,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
109682
109982
  const groups = declarations[event];
109683
109983
  need4(Array.isArray(groups), "DECLARATION_CHANGED");
109684
109984
  const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
109685
- need4(matches2.length === 1 && isDeepStrictEqual12(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
109985
+ need4(matches2.length === 1 && isDeepStrictEqual13(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
109686
109986
  expected.push({ event: nativeEvent, key: `${join58(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
109687
109987
  }
109688
109988
  codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
@@ -109748,7 +110048,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
109748
110048
  save(join58(journal, "hooks.before.json"), hooksFile.bytes);
109749
110049
  save(join58(journal, "policy.before.json"), policyFile.bytes);
109750
110050
  save(join58(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
109751
- need4(isDeepStrictEqual12(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
110051
+ need4(isDeepStrictEqual13(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
109752
110052
  checkInputs();
109753
110053
  let result2;
109754
110054
  try {
@@ -109762,7 +110062,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
109762
110062
  expectedConfig.hooks.state ??= {};
109763
110063
  for (const h of planned)
109764
110064
  expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
109765
- need4(isDeepStrictEqual12(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
110065
+ need4(isDeepStrictEqual13(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
109766
110066
  const savedConfig = snapshot(join58(journal, "config.before.toml"), true);
109767
110067
  need4(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
109768
110068
  const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
@@ -109770,8 +110070,8 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
109770
110070
  unchanged2(hooksFile);
109771
110071
  assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
109772
110072
  const current = await list2(), confirmed = admit(current);
109773
- need4(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual12(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
109774
- need4(isDeepStrictEqual12(current.filter((h) => !keys3.includes(h.key)), discovered.filter((h) => !keys3.includes(h.key))), "UNRELATED_HOOK_CHANGED");
110073
+ need4(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual13(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
110074
+ need4(isDeepStrictEqual13(current.filter((h) => !keys3.includes(h.key)), discovered.filter((h) => !keys3.includes(h.key))), "UNRELATED_HOOK_CHANGED");
109775
110075
  unchanged2(after2);
109776
110076
  unchanged2(savedConfig);
109777
110077
  unchanged2(hooksFile);
@@ -109838,6 +110138,9 @@ function hookFailureReason(error61, profileId) {
109838
110138
  if (code2 === "PROFILE_LOCK_MISMATCH") {
109839
110139
  return `${prefix} This session or project is pinned to a different profile. Inspect the session with skills sessions show <session-id> --json; review skills sessions reconcile for an intentional session change, or explicit project sync for a project change. Sync alone does not change that pin.`;
109840
110140
  }
110141
+ if (code2 === "SESSION_RECONCILIATION_REQUIRED") {
110142
+ return `${prefix} A loaded skill changed or was removed. Inspect this session with skills sessions show <session-id> --json, then review skills sessions reconcile for an intentional change. Sync alone does not change this pin.`;
110143
+ }
109841
110144
  if (code2.startsWith("SESSION_")) {
109842
110145
  return `${prefix} Inspect the session with skills sessions show <session-id> --json and review its parent or concurrent writer before retrying. Do not remove or rewrite its receipt to bypass this refusal.`;
109843
110146
  }
@@ -109880,9 +110183,11 @@ var init_hook_diagnostics = __esm(() => {
109880
110183
  "SESSION_GENERATION_EXHAUSTED",
109881
110184
  "SESSION_PARENT_CHANGED",
109882
110185
  "SESSION_RECEIPT_CHANGED",
110186
+ "SESSION_RECONCILIATION_REQUIRED",
109883
110187
  "SESSION_RECONCILIATION_INCOMPLETE",
109884
110188
  "SESSION_WRITE_LOCKED",
109885
- "SESSION_WRITE_LOCK_CHANGED"
110189
+ "SESSION_WRITE_LOCK_CHANGED",
110190
+ "SESSION_LOCK_RECOVERY_INCOMPLETE"
109886
110191
  ]);
109887
110192
  HookDiagnosticError = class HookDiagnosticError extends Error {
109888
110193
  code;
@@ -109900,7 +110205,7 @@ var exports_agent_integration = {};
109900
110205
  __export(exports_agent_integration, {
109901
110206
  registerAgentIntegration: () => registerAgentIntegration
109902
110207
  });
109903
- import { readFileSync as readFileSync39 } from "fs";
110208
+ import { readFileSync as readFileSync40 } from "fs";
109904
110209
  import { isAbsolute as isAbsolute27 } from "path";
109905
110210
  function pinnedHookProfile(input3, configuredProfile) {
109906
110211
  try {
@@ -109923,7 +110228,7 @@ async function contextForHook(input3, profileId, cached2, deadline) {
109923
110228
  throw new HookDiagnosticError("SKILLS_HOOK_TIMEOUT", "context");
109924
110229
  const args2 = [process.execPath, process.argv[1], "context", "--stdin", "--json", "--selection-profile", profileId];
109925
110230
  if (cached2)
109926
- args2.push("--cached");
110231
+ args2.push("--cached", "--auto-reconcile-safe");
109927
110232
  const child = Bun.spawn(args2, { stdin: new Blob([JSON.stringify(input3)]), stdout: "pipe", stderr: "pipe", env: { ...process.env, NO_COLOR: "1" } });
109928
110233
  let timedOut = false;
109929
110234
  const timer = setTimeout(() => {
@@ -109978,7 +110283,7 @@ function registerAgentIntegration(parent) {
109978
110283
  });
109979
110284
  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) => {
109980
110285
  try {
109981
- const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync39(options.discoveryInputs, "utf8")) : undefined;
110286
+ const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync40(options.discoveryInputs, "utf8")) : undefined;
109982
110287
  const plan = planAgentIntegration({ agents: agents(options.agent), command: options.command, profileId: options.selectionProfile, includeVendor: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases });
109983
110288
  const result2 = options.apply ? applyAgentIntegration(plan) : { changed: [], backups: [] };
109984
110289
  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) };
@@ -110036,7 +110341,7 @@ function registerAgentIntegration(parent) {
110036
110341
  try {
110037
110342
  if (agents(options.agent).length !== 1)
110038
110343
  throw new Error("A hook invocation requires one agent");
110039
- const inputText = readFileSync39(0, "utf8");
110344
+ const inputText = readFileSync40(0, "utf8");
110040
110345
  if (inputText.length > 1024 * 1024)
110041
110346
  throw new Error("Hook input is too large");
110042
110347
  let input3 = JSON.parse(inputText);
@@ -110130,13 +110435,18 @@ function registerAgentIntegration(parent) {
110130
110435
  }
110131
110436
  });
110132
110437
  const migrate = parent.command("migrate").description("Preserve and retire native agent skill copies");
110133
- migrate.command("native").option("--project <directory>", "Also inventory a project and its ancestors (the current directory and its ancestors are always included)").option("--include-unmanaged", "Archive user-authored skills as well as Skills-managed copies", false).option("--include-vendor", "Retire vendor SKILL.md discovery files while preserving plugin scripts and assets", false).option("--target-manifest <file>", "Reviewed exact native migration target manifest").option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Move selected skills to private archives outside agent discovery roots", false).option("--json", "Output inventory and archive receipt as JSON", false).description("Inventory native skill copies; preserve complete directories before retiring them").action(async (options) => {
110438
+ migrate.command("native").option("--agent <agent>", "Limit inventory and migration to one maintained agent").option("--project <directory>", "Also inventory a project and its ancestors (the current directory and its ancestors are always included)").option("--include-unmanaged", "Archive user-authored skills as well as Skills-managed copies", false).option("--include-vendor", "Retire vendor SKILL.md discovery files while preserving plugin scripts and assets", false).option("--reviewed-cache-alias <path>", "Exact skill-containing vendor cache alias reviewed for migration; runtime hooks still refuse it").option("--target-manifest <file>", "Reviewed exact native migration target manifest").option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Move selected skills to private archives outside agent discovery roots", false).option("--json", "Output inventory and archive receipt as JSON", false).description("Inventory native skill copies; preserve complete directories before retiring them").action(async (options) => {
110134
110439
  try {
110135
- const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync39(options.discoveryInputs, "utf8")) : undefined;
110440
+ if (options.agent && !INTEGRATION_AGENTS.includes(options.agent))
110441
+ throw new Error(`Unsupported migration agent: ${options.agent}`);
110442
+ const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync40(options.discoveryInputs, "utf8")) : undefined;
110136
110443
  if (options.targetManifest && (options.includeUnmanaged || options.includeVendor))
110137
110444
  throw new Error("--target-manifest cannot be combined with broad native migration selectors");
110138
110445
  const targetManifest = options.targetManifest ? readNativeMigrationTargetManifest(options.targetManifest) : undefined;
110139
- const inventory = inventoryNativeSkills(undefined, { projectDirs: [process.cwd(), ...options.project ? [options.project] : []], includeVendor: options.includeVendor, configured: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases });
110446
+ if (options.apply && options.reviewedCacheAlias && !targetManifest)
110447
+ throw new Error("Applying a reviewed cache alias requires an exact --target-manifest");
110448
+ const includeVendor = options.includeVendor || Boolean(targetManifest?.targets.some((target) => target.vendor));
110449
+ const inventory = inventoryNativeSkills(undefined, { projectDirs: [process.cwd(), ...options.project ? [options.project] : []], agents: options.agent ? [options.agent] : undefined, includeVendor, configured: includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias });
110140
110450
  if (targetManifest)
110141
110451
  selectNativeMigrationTargets(inventory, targetManifest);
110142
110452
  const result2 = options.apply ? archiveNativeSkills(inventory, { includeUnmanaged: options.includeUnmanaged, includeVendor: options.includeVendor, allowRootAliases: options.allowRootAliases, targetManifest }) : { entries: [], ...targetManifest ? { targetManifest: { schema: targetManifest.schema, digest: targetManifest.digest, targetCount: targetManifest.targets.length } } : {} };
@@ -110439,7 +110749,7 @@ import {
110439
110749
  copyFileSync as copyFileSync2,
110440
110750
  mkdirSync as mkdirSync27,
110441
110751
  readdirSync as readdirSync22,
110442
- readFileSync as readFileSync40,
110752
+ readFileSync as readFileSync41,
110443
110753
  statSync as statSync22,
110444
110754
  writeFileSync as writeFileSync27
110445
110755
  } from "fs";
@@ -110455,7 +110765,7 @@ function readSnapshotManifest(repoRoot, stationId) {
110455
110765
  const manifestPath = join59(snapshotRoot, "sync-manifest.json");
110456
110766
  let manifest;
110457
110767
  try {
110458
- manifest = JSON.parse(readFileSync40(manifestPath, "utf8"));
110768
+ manifest = JSON.parse(readFileSync41(manifestPath, "utf8"));
110459
110769
  } catch (error61) {
110460
110770
  fail5("MANIFEST_UNREADABLE", `cannot read snapshot manifest: ${manifestPath}: ${error61.message}`);
110461
110771
  }
@@ -110591,7 +110901,7 @@ function planStationHydration(stationId, repoRoot) {
110591
110901
  for (const copy of copies) {
110592
110902
  let isStub = false;
110593
110903
  try {
110594
- isStub = isPointerSkillMd(readFileSync40(copy.fullPath, "utf8"));
110904
+ isStub = isPointerSkillMd(readFileSync41(copy.fullPath, "utf8"));
110595
110905
  } catch {
110596
110906
  isStub = false;
110597
110907
  }
@@ -110795,7 +111105,7 @@ var init_hydrate = __esm(() => {
110795
111105
 
110796
111106
  // src/lib/prepare-skill.ts
110797
111107
  import { createHash as createHash30, randomUUID as randomUUID11 } from "crypto";
110798
- import { chmodSync as chmodSync4, existsSync as existsSync34, lstatSync as lstatSync30, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync6, readFileSync as readFileSync41, readdirSync as readdirSync23, renameSync as renameSync13, rmSync as rmSync9, writeFileSync as writeFileSync28 } from "fs";
111108
+ 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";
110799
111109
  import { tmpdir as tmpdir2 } from "os";
110800
111110
  import { dirname as dirname34, isAbsolute as isAbsolute28, join as join60, relative as relative9, resolve as resolve25, sep as sep12 } from "path";
110801
111111
  function prepareSkill(name, options) {
@@ -111015,7 +111325,7 @@ function snapshot2(root2) {
111015
111325
  bytes += stat3.size;
111016
111326
  if (bytes > 50000000)
111017
111327
  throw new Error("Skill exceeds the preparation size limit (50000000 bytes).");
111018
- const content = readFileSync41(absolute);
111328
+ const content = readFileSync42(absolute);
111019
111329
  if (content.length !== stat3.size)
111020
111330
  throw new Error("Skill changed during preparation.");
111021
111331
  files.push({ path, bytes: content, mode: stat3.mode & 511 });
@@ -111697,8 +112007,8 @@ __export(exports_publish, {
111697
112007
  PushSkillError: () => PushSkillError
111698
112008
  });
111699
112009
  import { execFileSync as execFileSync2 } from "child_process";
111700
- import { existsSync as existsSync35, readFileSync as readFileSync42 } from "fs";
111701
- import { hostname as hostname4 } from "os";
112010
+ import { existsSync as existsSync35, readFileSync as readFileSync43 } from "fs";
112011
+ import { hostname as hostname5 } from "os";
111702
112012
  import { join as join61 } from "path";
111703
112013
  function registerPublish(parent) {
111704
112014
  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) => {
@@ -111752,7 +112062,7 @@ async function pushSkill(name, options = {}) {
111752
112062
  await verifyPackedCanonicalHash(skill.name, packed);
111753
112063
  const versionManifest = buildVersionManifest(skill.path, packed);
111754
112064
  const skillMdPath = join61(skill.path, "SKILL.md");
111755
- const skillMd = existsSync35(skillMdPath) ? readFileSync42(skillMdPath, "utf-8") : undefined;
112065
+ const skillMd = existsSync35(skillMdPath) ? readFileSync43(skillMdPath, "utf-8") : undefined;
111756
112066
  const base3 = {
111757
112067
  slug: skill.name,
111758
112068
  path: skill.path,
@@ -111882,7 +112192,7 @@ function buildVersionManifest(skillDir, packed) {
111882
112192
  unpackedByteSize: packed.unpackedByteSize,
111883
112193
  bundleSha256: packed.sha256,
111884
112194
  provenance: {
111885
- machine: hostname4(),
112195
+ machine: hostname5(),
111886
112196
  agent: process.env.SKILLS_AGENT_ID ?? process.env.HASNA_AGENT_ID ?? process.env.AGENT_ID ?? null,
111887
112197
  cliVersion: package_default.version,
111888
112198
  gitRemote: sanitizeGitRemote(gitValue(skillDir, ["remote", "get-url", "origin"])),
@@ -113471,7 +113781,7 @@ var init_storage2 = __esm(() => {
113471
113781
  });
113472
113782
 
113473
113783
  // src/lib/registry-reconcile.ts
113474
- import { existsSync as existsSync36, readFileSync as readFileSync43, statSync as statSync23, writeFileSync as writeFileSync30 } from "fs";
113784
+ import { existsSync as existsSync36, readFileSync as readFileSync44, statSync as statSync23, writeFileSync as writeFileSync30 } from "fs";
113475
113785
  import { join as join62 } from "path";
113476
113786
  function isDirectory2(path) {
113477
113787
  try {
@@ -113491,7 +113801,7 @@ function readBaseline(skillDir) {
113491
113801
  if (!existsSync36(markerPath))
113492
113802
  return;
113493
113803
  try {
113494
- const marker = JSON.parse(readFileSync43(markerPath, "utf-8"));
113804
+ const marker = JSON.parse(readFileSync44(markerPath, "utf-8"));
113495
113805
  if (!isSkillsOwnershipMarker(marker))
113496
113806
  return;
113497
113807
  return {
@@ -113507,7 +113817,7 @@ function readCursor(root2) {
113507
113817
  if (!existsSync36(path))
113508
113818
  return { runCount: 0 };
113509
113819
  try {
113510
- const cursor2 = JSON.parse(readFileSync43(path, "utf-8"));
113820
+ const cursor2 = JSON.parse(readFileSync44(path, "utf-8"));
113511
113821
  return { runCount: typeof cursor2.runCount === "number" ? cursor2.runCount : 0 };
113512
113822
  } catch {
113513
113823
  return { runCount: 0 };