@hasna/skills 0.10.1 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6988,7 +6988,7 @@ var require_public_api = __commonJS((exports) => {
6988
6988
  });
6989
6989
 
6990
6990
  // src/lib/registry.ts
6991
- import { existsSync as existsSync14, readFileSync as readFileSync17, readdirSync as readdirSync11 } from "fs";
6991
+ import { existsSync as existsSync14, readFileSync as readFileSync18, readdirSync as readdirSync11 } from "fs";
6992
6992
  import { join as join29 } from "path";
6993
6993
 
6994
6994
  // src/lib/config.ts
@@ -9091,7 +9091,7 @@ function assertAgentPolicyCollections(policy) {
9091
9091
 
9092
9092
  // src/lib/agent-discovery.ts
9093
9093
  import { createHash as createHash16 } from "crypto";
9094
- import { lstatSync as lstatSync20, readFileSync as readFileSync16, readdirSync as readdirSync9, realpathSync as realpathSync4 } from "fs";
9094
+ import { lstatSync as lstatSync20, readFileSync as readFileSync17, readdirSync as readdirSync9, realpathSync as realpathSync4 } from "fs";
9095
9095
  import { basename as basename8, dirname as dirname16, isAbsolute as isAbsolute14, join as join26, resolve as resolve14, sep as sep5 } from "path";
9096
9096
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
9097
9097
 
@@ -9611,7 +9611,7 @@ import { isAbsolute as isAbsolute11, join as join23, resolve as resolve11 } from
9611
9611
  // package.json
9612
9612
  var package_default = {
9613
9613
  name: "@hasna/skills",
9614
- version: "0.10.1",
9614
+ version: "0.10.2",
9615
9615
  description: "Skills library for AI coding agents",
9616
9616
  type: "module",
9617
9617
  bin: {
@@ -11823,7 +11823,9 @@ function tarOctal(field) {
11823
11823
 
11824
11824
  // src/lib/selection-cache.ts
11825
11825
  import { createHash as createHash7, randomUUID } from "crypto";
11826
- import { closeSync as closeSync6, constants as constants3, fstatSync as fstatSync6, fsyncSync, lstatSync as lstatSync9, mkdirSync as mkdirSync3, openSync as openSync6, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
11826
+ import { spawnSync as spawnSync2 } from "child_process";
11827
+ import { closeSync as closeSync6, constants as constants3, fstatSync as fstatSync6, fsyncSync, lstatSync as lstatSync9, mkdirSync as mkdirSync3, openSync as openSync6, readFileSync as readFileSync9, readlinkSync as readlinkSync2, readSync as readSync3, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
11828
+ import { hostname } from "os";
11827
11829
  import { dirname as dirname8, join as join12, parse as parse2, resolve as resolve7 } from "path";
11828
11830
  import { isDeepStrictEqual } from "util";
11829
11831
 
@@ -12124,6 +12126,166 @@ function readSkillSessionSnapshot(sessionId, options = {}) {
12124
12126
  function skillSessionSnapshotBinding(snapshot) {
12125
12127
  return { sessionId: snapshot.sessionId, generation: snapshot.generation, receiptSha256: snapshot.receiptSha256 };
12126
12128
  }
12129
+ var SESSION_LOCK_MAX_BYTES = 4096;
12130
+ var SESSION_LOCK_MIN_AGE_MS = 30000;
12131
+ function localBootId() {
12132
+ try {
12133
+ if (process.platform === "linux") {
12134
+ const value = readFileSync9("/proc/sys/kernel/random/boot_id", "utf8").trim();
12135
+ return /^[a-f0-9-]{36}$/i.test(value) ? value : null;
12136
+ }
12137
+ if (process.platform === "darwin") {
12138
+ const result = spawnSync2("/usr/sbin/sysctl", ["-n", "kern.boottime"], { encoding: "utf8", timeout: 3000, env: { LC_ALL: "C" } });
12139
+ if (result.status !== 0 || result.error)
12140
+ return null;
12141
+ const seconds = /\bsec\s*=\s*(\d+)\b/.exec(result.stdout)?.[1];
12142
+ return seconds ? `darwin:${seconds}` : null;
12143
+ }
12144
+ } catch {}
12145
+ return null;
12146
+ }
12147
+ function processStart(pid) {
12148
+ if (!Number.isSafeInteger(pid) || pid <= 0)
12149
+ return { state: "unknown" };
12150
+ if (process.platform === "linux") {
12151
+ try {
12152
+ const stat = readFileSync9(`/proc/${pid}/stat`, "utf8");
12153
+ const close = stat.lastIndexOf(")");
12154
+ const start = close < 0 ? undefined : stat.slice(close + 2).trim().split(/\s+/)[19];
12155
+ return start && /^\d+$/.test(start) ? { state: "alive", start: `linux:${start}` } : { state: "unknown" };
12156
+ } catch (error) {
12157
+ return { state: error.code === "ENOENT" ? "absent" : "unknown" };
12158
+ }
12159
+ }
12160
+ if (process.platform === "darwin") {
12161
+ const result = spawnSync2("/bin/ps", ["-p", String(pid), "-o", "lstart="], { encoding: "utf8", timeout: 3000, env: { LC_ALL: "C" } });
12162
+ if (result.error || result.signal)
12163
+ return { state: "unknown" };
12164
+ const start = result.stdout.trim();
12165
+ if (result.status === 1 && !start && !result.stderr.trim())
12166
+ return { state: "absent" };
12167
+ return result.status === 0 && start ? { state: "alive", start: `darwin:${start}` } : { state: "unknown" };
12168
+ }
12169
+ return { state: "unknown" };
12170
+ }
12171
+ function localLockIdentity() {
12172
+ const bootId = localBootId(), owner = processStart(process.pid);
12173
+ if (!bootId || owner.state !== "alive")
12174
+ return null;
12175
+ let pidNamespace;
12176
+ if (process.platform === "linux") {
12177
+ try {
12178
+ pidNamespace = readlinkSync2("/proc/self/ns/pid");
12179
+ } catch {
12180
+ return null;
12181
+ }
12182
+ if (!/^pid:\[\d+\]$/.test(pidNamespace))
12183
+ return null;
12184
+ }
12185
+ return { host: hostname(), bootId, pidNamespace, processStart: owner.start };
12186
+ }
12187
+ function readSessionLock(sessionId, options) {
12188
+ const path = `${sessionReceiptPath(sessionId, options)}.write-lock`;
12189
+ const before = lstatSync9(path, { throwIfNoEntry: false });
12190
+ if (!before)
12191
+ return null;
12192
+ if (!before.isFile() || before.nlink !== 1 || process.getuid && before.uid !== process.getuid()) {
12193
+ throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is not a private regular file; recovery requires review.");
12194
+ }
12195
+ const bytes = readRegularFile(path, SESSION_LOCK_MAX_BYTES);
12196
+ const after = lstatSync9(path, { throwIfNoEntry: false });
12197
+ if (!bytes || !after?.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs) {
12198
+ throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock changed during inspection; recovery requires review.");
12199
+ }
12200
+ let marker;
12201
+ try {
12202
+ marker = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
12203
+ } catch {
12204
+ throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock is unreadable; recovery requires review.");
12205
+ }
12206
+ 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)) {
12207
+ throw new SkillSelectionError("SESSION_WRITE_LOCKED", "The session lock has invalid ownership metadata; recovery requires review.");
12208
+ }
12209
+ return { path, bytes, marker, sha256: sha256Hex(bytes), dev: after.dev, ino: after.ino, mtimeMs: after.mtimeMs };
12210
+ }
12211
+ function writerIsGone(lock, explicitLocalHost) {
12212
+ if (Date.now() - lock.mtimeMs < SESSION_LOCK_MIN_AGE_MS)
12213
+ return false;
12214
+ const owner = processStart(lock.marker.pid);
12215
+ if (owner.state === "unknown")
12216
+ return false;
12217
+ if (lock.marker.schemaVersion === 1)
12218
+ return explicitLocalHost && owner.state === "absent";
12219
+ const local = localLockIdentity();
12220
+ if (!local || lock.marker.host !== local.host || lock.marker.bootId !== local.bootId || lock.marker.pidNamespace !== local.pidNamespace)
12221
+ return false;
12222
+ return owner.state === "absent" || owner.state === "alive" && owner.start !== lock.marker.processStart;
12223
+ }
12224
+ function archiveSessionLock(lock, receipt, explicitLocalHost) {
12225
+ const guardPath = `${lock.path}.recovery-guard`;
12226
+ let guardFd;
12227
+ try {
12228
+ guardFd = openSync6(guardPath, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
12229
+ } catch (error) {
12230
+ if (error.code === "EEXIST")
12231
+ return null;
12232
+ throw error;
12233
+ }
12234
+ const guardStat = fstatSync6(guardFd);
12235
+ try {
12236
+ writeFileSync3(guardFd, `${JSON.stringify({ schemaVersion: 1, pid: process.pid, operationId: randomUUID() })}
12237
+ `);
12238
+ fsyncSync(guardFd);
12239
+ const cacheOptions = { cacheDir: dirname8(dirname8(lock.path)) };
12240
+ const current = readSessionLock(lock.marker.sessionId, cacheOptions);
12241
+ if (!current || current.dev !== lock.dev || current.ino !== lock.ino || current.sha256 !== lock.sha256 || current.mtimeMs !== lock.mtimeMs || !writerIsGone(current, explicitLocalHost))
12242
+ return null;
12243
+ const currentReceipt = readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions);
12244
+ if (currentReceipt?.sha256 !== receipt?.sha256 || currentReceipt?.generation !== receipt?.generation)
12245
+ return null;
12246
+ const directory = join12(dirname8(dirname8(lock.path)), "session-lock-recoveries", randomUUID());
12247
+ const archivePath = join12(directory, "original.write-lock");
12248
+ const recordPath = join12(directory, "recovery.json");
12249
+ assertRegularPath(archivePath, true);
12250
+ archiveBytes(recordPath, Buffer.from(`${JSON.stringify({
12251
+ schemaVersion: 1,
12252
+ sessionId: lock.marker.sessionId,
12253
+ originalPath: lock.path,
12254
+ lockSha256: lock.sha256,
12255
+ lockMtime: new Date(lock.mtimeMs).toISOString(),
12256
+ lockPid: lock.marker.pid,
12257
+ receiptSha256: receipt?.sha256 ?? null,
12258
+ receiptGeneration: receipt?.generation ?? null,
12259
+ status: "prepared"
12260
+ })}
12261
+ `));
12262
+ syncDirectory(directory);
12263
+ syncDirectory(dirname8(directory));
12264
+ const finalLock = readSessionLock(lock.marker.sessionId, cacheOptions);
12265
+ if (!finalLock || finalLock.dev !== lock.dev || finalLock.ino !== lock.ino || finalLock.sha256 !== lock.sha256 || readSkillSessionSnapshotIfExists(lock.marker.sessionId, cacheOptions)?.sha256 !== receipt?.sha256)
12266
+ return null;
12267
+ let moved = false;
12268
+ try {
12269
+ renameSync(lock.path, archivePath);
12270
+ moved = true;
12271
+ syncDirectory(directory);
12272
+ syncDirectory(dirname8(lock.path));
12273
+ if (sha256Hex(readRegularFile(archivePath, SESSION_LOCK_MAX_BYTES)) !== lock.sha256) {
12274
+ throw new Error("Archived lock bytes changed");
12275
+ }
12276
+ } catch (error) {
12277
+ if (moved)
12278
+ 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.");
12279
+ throw error;
12280
+ }
12281
+ return archivePath;
12282
+ } finally {
12283
+ closeSync6(guardFd);
12284
+ const current = lstatSync9(guardPath, { throwIfNoEntry: false });
12285
+ if (current?.isFile() && current.dev === guardStat.dev && current.ino === guardStat.ino)
12286
+ unlinkSync(guardPath);
12287
+ }
12288
+ }
12127
12289
  function withSessionWriteLocks(sessionIds, options, action) {
12128
12290
  const operationId = randomUUID();
12129
12291
  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);
@@ -12143,13 +12305,30 @@ function withSessionWriteLocks(sessionIds, options, action) {
12143
12305
  try {
12144
12306
  fd = openSync6(lock.path, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
12145
12307
  } catch (error) {
12146
- if (error.code === "EEXIST")
12147
- 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.");
12148
- throw error;
12308
+ if (error.code !== "EEXIST")
12309
+ throw error;
12310
+ try {
12311
+ const stale = readSessionLock(lock.sessionId, options);
12312
+ const receipt = stale && readSkillSessionSnapshotIfExists(lock.sessionId, options);
12313
+ if (stale?.marker.schemaVersion === 2 && writerIsGone(stale, false)) {
12314
+ archiveSessionLock(stale, receipt, false);
12315
+ }
12316
+ } catch (recoveryError) {
12317
+ if (recoveryError instanceof SkillSelectionError && recoveryError.code === "SESSION_LOCK_RECOVERY_INCOMPLETE")
12318
+ throw recoveryError;
12319
+ }
12320
+ try {
12321
+ fd = openSync6(lock.path, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | constants3.O_NOFOLLOW, 384);
12322
+ } catch (retryError) {
12323
+ if (retryError.code === "EEXIST")
12324
+ 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.");
12325
+ throw retryError;
12326
+ }
12149
12327
  }
12150
12328
  const stat = fstatSync6(fd);
12151
12329
  owned.push({ path: lock.path, fd, dev: stat.dev, ino: stat.ino });
12152
- writeFileSync3(fd, `${JSON.stringify({ schemaVersion: 1, operationId, sessionId: lock.sessionId, pid: process.pid })}
12330
+ const identity3 = localLockIdentity();
12331
+ writeFileSync3(fd, `${JSON.stringify({ schemaVersion: identity3 ? 2 : 1, operationId, sessionId: lock.sessionId, pid: process.pid, ...identity3 })}
12153
12332
  `);
12154
12333
  }
12155
12334
  return action(assertOwned);
@@ -12268,7 +12447,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
12268
12447
  }
12269
12448
 
12270
12449
  // src/lib/dependency-preparation.ts
12271
- import { constants as constants4, closeSync as closeSync7, fstatSync as fstatSync7, lstatSync as lstatSync10, mkdirSync as mkdirSync4, openSync as openSync7, readFileSync as readFileSync9, readdirSync as readdirSync5, rmdirSync, unlinkSync as unlinkSync2, ftruncateSync, writeSync } from "fs";
12450
+ import { constants as constants4, 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";
12272
12451
  import { join as join13 } from "path";
12273
12452
 
12274
12453
  // src/lib/preparation-process.ts
@@ -12342,7 +12521,7 @@ function claimPreparation(skillPath) {
12342
12521
  return;
12343
12522
  if (!lstatSync10(state).isFile() || lstatSync10(state).isSymbolicLink() || lstatSync10(state).size > 1024)
12344
12523
  return;
12345
- const previous = JSON.parse(readFileSync9(state, "utf8"));
12524
+ const previous = JSON.parse(readFileSync10(state, "utf8"));
12346
12525
  if (previous.version !== 1 || previous.status !== "failed" || Object.keys(previous).length !== 2)
12347
12526
  return;
12348
12527
  }
@@ -12475,11 +12654,11 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
12475
12654
  }
12476
12655
 
12477
12656
  // src/lib/skillinfo.ts
12478
- import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
12657
+ import { existsSync as existsSync12, readFileSync as readFileSync16 } from "fs";
12479
12658
  import { join as join20 } from "path";
12480
12659
 
12481
12660
  // src/lib/installer.ts
12482
- import { existsSync as existsSync11, readFileSync as readFileSync14, rmSync as rmSync2 } from "fs";
12661
+ import { existsSync as existsSync11, readFileSync as readFileSync15, rmSync as rmSync2 } from "fs";
12483
12662
  import { join as join19 } from "path";
12484
12663
  import { homedir as homedir4 } from "os";
12485
12664
 
@@ -12490,7 +12669,7 @@ import {
12490
12669
  lstatSync as lstatSync15,
12491
12670
  mkdirSync as mkdirSync6,
12492
12671
  mkdtempSync,
12493
- readFileSync as readFileSync12,
12672
+ readFileSync as readFileSync13,
12494
12673
  readdirSync as readdirSync8,
12495
12674
  renameSync as renameSync3,
12496
12675
  rmSync,
@@ -12502,7 +12681,7 @@ import { basename as basename5, dirname as dirname11, join as join17 } from "pat
12502
12681
 
12503
12682
  // src/lib/agent-integration.ts
12504
12683
  import { createHash as createHash10, randomUUID as randomUUID2 } from "crypto";
12505
- import { existsSync as existsSync8, lstatSync as lstatSync14, statSync as statSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync11, 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 constants7, linkSync } from "fs";
12684
+ import { existsSync as existsSync8, lstatSync as lstatSync14, statSync as statSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync12, 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 constants7, linkSync } from "fs";
12506
12685
  import { dirname as dirname10, join as join16, resolve as resolve9, relative as relative4, isAbsolute as isAbsolute9, sep as sep4 } from "path";
12507
12686
  import { homedir as homedir2 } from "os";
12508
12687
 
@@ -12588,7 +12767,7 @@ function requiresCliSkillLoading(dataDir2 = getDataDirReadOnly()) {
12588
12767
 
12589
12768
  // src/lib/agent-bridge.ts
12590
12769
  import { createHash as createHash8 } from "crypto";
12591
- import { lstatSync as lstatSync12, readFileSync as readFileSync10, readdirSync as readdirSync6 } from "fs";
12770
+ import { lstatSync as lstatSync12, readFileSync as readFileSync11, readdirSync as readdirSync6 } from "fs";
12592
12771
  import { join as join15 } from "path";
12593
12772
  var CLI_BRIDGE_NAME = "skills-cli";
12594
12773
  var CLI_BRIDGE_VERSION = 1;
@@ -12622,7 +12801,7 @@ function isOwnedCliBridge(path, expectedPaths) {
12622
12801
  return false;
12623
12802
  return Object.entries(CLI_BRIDGE_FILES).every(([name, content]) => {
12624
12803
  const file = join15(path, name), stat = lstatSync12(file);
12625
- return stat.isFile() && !stat.isSymbolicLink() && stat.size === Buffer.byteLength(content) && readFileSync10(file, "utf8") === content;
12804
+ return stat.isFile() && !stat.isSymbolicLink() && stat.size === Buffer.byteLength(content) && readFileSync11(file, "utf8") === content;
12626
12805
  });
12627
12806
  } catch {
12628
12807
  return false;
@@ -13022,7 +13201,7 @@ function readRootAlias(home, agent) {
13022
13201
  const stat = lstatSync14(alias, { throwIfNoEntry: false, bigint: true });
13023
13202
  if (!stat?.isSymbolicLink())
13024
13203
  throw new Error(`Agent root alias changed after planning: ${alias}`);
13025
- const link = readlinkSync2(alias), targetPath = resolve9(dirname10(alias), link);
13204
+ const link = readlinkSync3(alias), targetPath = resolve9(dirname10(alias), link);
13026
13205
  assertSafePath(targetPath);
13027
13206
  const targetStat = lstatSync14(targetPath, { throwIfNoEntry: false });
13028
13207
  if (!targetStat?.isDirectory())
@@ -13175,7 +13354,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
13175
13354
  throw new Error("Native skill discovery metadata limit exceeded");
13176
13355
  }
13177
13356
  function emptySiblingCacheAlias(path, parent) {
13178
- const link = readlinkSync2(path), target = resolve9(parent, link);
13357
+ const link = readlinkSync3(path), target = resolve9(parent, link);
13179
13358
  if (dirname10(target) !== parent || (isAbsolute9(link) ? link !== target : dirname10(link) !== "."))
13180
13359
  return false;
13181
13360
  if (!lstatSync14(target, { throwIfNoEntry: false })?.isDirectory())
@@ -13204,7 +13383,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
13204
13383
  const marker = join16(path, ".hasna-skills.json");
13205
13384
  if (existsSync8(marker)) {
13206
13385
  try {
13207
- managed = JSON.parse(readFileSync11(marker, "utf8")).managedBy === "@hasna/skills";
13386
+ managed = JSON.parse(readFileSync12(marker, "utf8")).managedBy === "@hasna/skills";
13208
13387
  } catch {}
13209
13388
  }
13210
13389
  const bridge = !vendor && isOwnedCliBridge(path, bridgePaths);
@@ -13277,7 +13456,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
13277
13456
  }
13278
13457
  function readOptional(path) {
13279
13458
  assertSafePath(path);
13280
- return existsSync8(path) ? readFileSync11(path, "utf8") : null;
13459
+ return existsSync8(path) ? readFileSync12(path, "utf8") : null;
13281
13460
  }
13282
13461
  function jsonObject(text2, path) {
13283
13462
  if (text2 === null)
@@ -13989,7 +14168,7 @@ function hasSkillsOwnershipMarker(dir) {
13989
14168
  try {
13990
14169
  if (!lstatSync15(path).isFile())
13991
14170
  return false;
13992
- const marker = JSON.parse(readFileSync12(path, "utf8"));
14171
+ const marker = JSON.parse(readFileSync13(path, "utf8"));
13993
14172
  return isSkillsOwnershipMarker(marker);
13994
14173
  } catch {
13995
14174
  return false;
@@ -14198,7 +14377,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
14198
14377
  if (dirExists && managed && hasSkillMd && !options.force && isPointerSkillMd(skillMd)) {
14199
14378
  let existingIsStub = false;
14200
14379
  try {
14201
- existingIsStub = isPointerSkillMd(readFileSync12(skillMdPath, "utf-8"));
14380
+ existingIsStub = isPointerSkillMd(readFileSync13(skillMdPath, "utf-8"));
14202
14381
  } catch {
14203
14382
  existingIsStub = false;
14204
14383
  }
@@ -14278,7 +14457,7 @@ function sourceSkillMd(skillPath, name, description, kind, preferBundledDocs = f
14278
14457
  if (kind === undefined || kind === "instruction" || preferBundledDocs) {
14279
14458
  const skillMdPath = join17(skillPath, "SKILL.md");
14280
14459
  if (existsSync9(skillMdPath))
14281
- return readFileSync12(skillMdPath, "utf-8");
14460
+ return readFileSync13(skillMdPath, "utf-8");
14282
14461
  }
14283
14462
  return pointerSkillMd(name, description);
14284
14463
  }
@@ -14318,7 +14497,7 @@ function resolveSkillAlias(name) {
14318
14497
  }
14319
14498
 
14320
14499
  // src/lib/project-state.ts
14321
- import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "fs";
14500
+ import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
14322
14501
  import { join as join18 } from "path";
14323
14502
  var VALID_PIN_SOURCES = [
14324
14503
  "official",
@@ -14344,7 +14523,7 @@ function loadProjectConfig(targetDir = process.cwd()) {
14344
14523
  if (!existsSync10(path))
14345
14524
  return null;
14346
14525
  try {
14347
- return normalizeProjectConfig(JSON.parse(readFileSync13(path, "utf-8")));
14526
+ return normalizeProjectConfig(JSON.parse(readFileSync14(path, "utf-8")));
14348
14527
  } catch {
14349
14528
  return null;
14350
14529
  }
@@ -14531,7 +14710,7 @@ function createLocalSkillManifest(name, generateSkillMd) {
14531
14710
  let skillMd = "";
14532
14711
  const skillMdPath = join19(sourcePath, "SKILL.md");
14533
14712
  if (existsSync11(skillMdPath)) {
14534
- skillMd = readFileSync14(skillMdPath, "utf-8");
14713
+ skillMd = readFileSync15(skillMdPath, "utf-8");
14535
14714
  } else if (generateSkillMd) {
14536
14715
  skillMd = generateSkillMd(name) ?? "";
14537
14716
  } else {
@@ -14650,7 +14829,7 @@ function resolveAgentSkillMd(name, generateSkillMd) {
14650
14829
  const sourcePath = getSkillPath(name);
14651
14830
  const skillMdPath = join19(sourcePath, "SKILL.md");
14652
14831
  if (existsSync11(skillMdPath))
14653
- return readFileSync14(skillMdPath, "utf-8");
14832
+ return readFileSync15(skillMdPath, "utf-8");
14654
14833
  if (generateSkillMd)
14655
14834
  return generateSkillMd(name);
14656
14835
  return generateMinimalSkillMd(name);
@@ -14706,14 +14885,14 @@ function readBundledSkillVersion(name) {
14706
14885
  if (!existsSync11(pkgPath))
14707
14886
  return "unknown";
14708
14887
  try {
14709
- const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
14888
+ const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
14710
14889
  return pkg.version || "unknown";
14711
14890
  } catch {
14712
14891
  return "unknown";
14713
14892
  }
14714
14893
  }
14715
14894
  function readFileIfExists(path) {
14716
- return existsSync11(path) ? readFileSync14(path, "utf-8") : null;
14895
+ return existsSync11(path) ? readFileSync15(path, "utf-8") : null;
14717
14896
  }
14718
14897
  function loadProjectConfigCompat(targetDir) {
14719
14898
  return loadProjectConfig(targetDir);
@@ -14761,7 +14940,7 @@ function isInstructionSkillDir(skillPath, meta) {
14761
14940
  if (!existsSync12(skillMdPath))
14762
14941
  return false;
14763
14942
  try {
14764
- return parseSkillFrontmatter(readFileSync15(skillMdPath, "utf-8"))?.kind === "instruction";
14943
+ return parseSkillFrontmatter(readFileSync16(skillMdPath, "utf-8"))?.kind === "instruction";
14765
14944
  } catch {
14766
14945
  return false;
14767
14946
  }
@@ -14812,7 +14991,7 @@ function getSkillRequirements(name) {
14812
14991
  const meta = getSkill(name);
14813
14992
  let dependencies = {};
14814
14993
  try {
14815
- dependencies = JSON.parse(readFileSync15(join20(skillPath, "package.json"), "utf8")).dependencies || {};
14994
+ dependencies = JSON.parse(readFileSync16(join20(skillPath, "package.json"), "utf8")).dependencies || {};
14816
14995
  } catch {}
14817
14996
  return getSkillRequirementsFromContent(name, texts, dependencies, meta);
14818
14997
  }
@@ -14880,7 +15059,7 @@ async function runSkill(name, args, options = {}) {
14880
15059
  }
14881
15060
  let entryPoint2;
14882
15061
  try {
14883
- const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
15062
+ const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
14884
15063
  if (pkg.bin) {
14885
15064
  const binValues = Object.values(pkg.bin);
14886
15065
  entryPoint2 = binValues[0];
@@ -14983,7 +15162,7 @@ function generateSkillMd(name) {
14983
15162
  const pkgPath = join20(skillPath, "package.json");
14984
15163
  if (existsSync12(pkgPath)) {
14985
15164
  try {
14986
- const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
15165
+ const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
14987
15166
  if (pkg.bin) {
14988
15167
  const binKeys = Object.keys(pkg.bin);
14989
15168
  if (binKeys.length > 0)
@@ -15059,7 +15238,7 @@ function extractEnvVars(text2) {
15059
15238
  function readIfExists(path) {
15060
15239
  try {
15061
15240
  if (existsSync12(path)) {
15062
- return readFileSync15(path, "utf-8");
15241
+ return readFileSync16(path, "utf-8");
15063
15242
  }
15064
15243
  } catch {}
15065
15244
  return null;
@@ -16569,7 +16748,7 @@ function read2(path, changes) {
16569
16748
  return null;
16570
16749
  if (!stat2.isFile() || stat2.size > 16 * 1024 * 1024)
16571
16750
  throw new Error(`Unsupported or oversized native discovery input: ${path}`);
16572
- return readFileSync16(path, "utf8");
16751
+ return readFileSync17(path, "utf8");
16573
16752
  }
16574
16753
  function projected(source, changes, budget = discoveryByteBudget()) {
16575
16754
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") {
@@ -17660,7 +17839,7 @@ function discoverSkillsInDir(dir, source = "custom") {
17660
17839
  continue;
17661
17840
  let content;
17662
17841
  try {
17663
- content = readFileSync17(skillMdPath, "utf-8");
17842
+ content = readFileSync18(skillMdPath, "utf-8");
17664
17843
  } catch {
17665
17844
  continue;
17666
17845
  }
@@ -17697,7 +17876,7 @@ function findExtensionSkillPath(name) {
17697
17876
  continue;
17698
17877
  let content;
17699
17878
  try {
17700
- content = readFileSync17(skillMdPath, "utf-8");
17879
+ content = readFileSync18(skillMdPath, "utf-8");
17701
17880
  } catch {
17702
17881
  continue;
17703
17882
  }
@@ -17762,7 +17941,7 @@ function getAllTags() {
17762
17941
  }
17763
17942
  // src/lib/run-state.ts
17764
17943
  import { createHash as createHash17, randomBytes } from "crypto";
17765
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync18, readdirSync as readdirSync12, statSync as statSync10, writeFileSync as writeFileSync9 } from "fs";
17944
+ import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync19, readdirSync as readdirSync12, statSync as statSync10, writeFileSync as writeFileSync9 } from "fs";
17766
17945
  import { extname, join as join30, relative as relative6 } from "path";
17767
17946
  function createSkillRun(params, targetDir = process.cwd()) {
17768
17947
  const now = new Date;
@@ -17834,7 +18013,7 @@ function appendRunEvent(context, event, data = {}) {
17834
18013
  const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
17835
18014
  `;
17836
18015
  const path = join30(context.runDir, "events.ndjson");
17837
- const previous = existsSync15(path) ? readFileSync18(path, "utf-8") : "";
18016
+ const previous = existsSync15(path) ? readFileSync19(path, "utf-8") : "";
17838
18017
  writeFileSync9(path, previous + line);
17839
18018
  }
17840
18019
  function listSkillRuns(targetDir = process.cwd(), limit = 50) {
@@ -17884,7 +18063,7 @@ function collectRunArtifacts(context) {
17884
18063
  const artifacts = [];
17885
18064
  for (const path of walkFiles(context.exportDir)) {
17886
18065
  const stat2 = statSync10(path);
17887
- const bytes = readFileSync18(path);
18066
+ const bytes = readFileSync19(path);
17888
18067
  artifacts.push({
17889
18068
  path: toProjectRelative(context.targetDir, path),
17890
18069
  mime: mimeForPath(path),
@@ -17899,7 +18078,7 @@ function readRunRecord(runDir) {
17899
18078
  if (!existsSync15(path))
17900
18079
  return null;
17901
18080
  try {
17902
- return JSON.parse(readFileSync18(path, "utf-8"));
18081
+ return JSON.parse(readFileSync19(path, "utf-8"));
17903
18082
  } catch {
17904
18083
  return null;
17905
18084
  }
@@ -18069,7 +18248,7 @@ __export(exports_external, {
18069
18248
  input: () => input,
18070
18249
  includes: () => _includes,
18071
18250
  httpUrl: () => httpUrl,
18072
- hostname: () => hostname2,
18251
+ hostname: () => hostname3,
18073
18252
  hex: () => hex2,
18074
18253
  hash: () => hash2,
18075
18254
  guid: () => guid2,
@@ -19855,7 +20034,7 @@ __export(exports_regexes, {
19855
20034
  idnEmail: () => idnEmail,
19856
20035
  httpProtocol: () => httpProtocol,
19857
20036
  html5Email: () => html5Email,
19858
- hostname: () => hostname,
20037
+ hostname: () => hostname2,
19859
20038
  hex: () => hex,
19860
20039
  guid: () => guid,
19861
20040
  extendedDuration: () => extendedDuration,
@@ -19917,7 +20096,7 @@ var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]
19917
20096
  var 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])$/;
19918
20097
  var base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
19919
20098
  var base64url = /^[A-Za-z0-9_-]*$/;
19920
- var 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])?)*\.?$/;
20099
+ var 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])?)*\.?$/;
19921
20100
  var domain = /^(?=.{1,253}$)([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,63}$/;
19922
20101
  var httpProtocol = /^https?$/;
19923
20102
  var e164 = /^\+[1-9]\d{6,14}$/;
@@ -20754,9 +20933,9 @@ var asciiTabOrNewline = /[\t\n\r]/g;
20754
20933
  function stripTabAndNewline(value) {
20755
20934
  return value.replace(asciiTabOrNewline, "");
20756
20935
  }
20757
- function urlHostnameOk(url, hostname2) {
20758
- hostname2.lastIndex = 0;
20759
- return hostname2.test(url.hostname);
20936
+ function urlHostnameOk(url, hostname3) {
20937
+ hostname3.lastIndex = 0;
20938
+ return hostname3.test(url.hostname);
20760
20939
  }
20761
20940
  function urlProtocolOk(url, protocol) {
20762
20941
  protocol.lastIndex = 0;
@@ -34144,7 +34323,7 @@ __export(exports_schemas2, {
34144
34323
  int: () => int,
34145
34324
  instanceof: () => _instanceof,
34146
34325
  httpUrl: () => httpUrl,
34147
- hostname: () => hostname2,
34326
+ hostname: () => hostname3,
34148
34327
  hex: () => hex2,
34149
34328
  hash: () => hash2,
34150
34329
  guid: () => guid2,
@@ -34854,7 +35033,7 @@ var ZodCustomStringFormat = /* @__PURE__ */ $constructor("ZodCustomStringFormat"
34854
35033
  function stringFormat(format, fnOrRegex, _params = {}) {
34855
35034
  return _stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params);
34856
35035
  }
34857
- function hostname2(_params) {
35036
+ function hostname3(_params) {
34858
35037
  return _stringFormat(ZodCustomStringFormat, "hostname", exports_regexes.hostname, _params);
34859
35038
  }
34860
35039
  function hex2(_params) {
@@ -39414,7 +39593,7 @@ async function createRemoteSkillsClient(env = process.env) {
39414
39593
  return connection ? new RemoteSkillsClient(connection.apiKey, connection.apiOrigin) : null;
39415
39594
  }
39416
39595
  // src/lib/scheduler.ts
39417
- import { existsSync as existsSync16, readFileSync as readFileSync19, writeFileSync as writeFileSync10, mkdirSync as mkdirSync11 } from "fs";
39596
+ import { existsSync as existsSync16, readFileSync as readFileSync20, writeFileSync as writeFileSync10, mkdirSync as mkdirSync11 } from "fs";
39418
39597
  import { join as join31 } from "path";
39419
39598
  function getSchedulesPath(targetDir = process.cwd()) {
39420
39599
  return join31(targetDir, ".skills", "schedules.json");
@@ -39423,7 +39602,7 @@ function loadSchedules(targetDir = process.cwd()) {
39423
39602
  const path = getSchedulesPath(targetDir);
39424
39603
  if (existsSync16(path)) {
39425
39604
  try {
39426
- return JSON.parse(readFileSync19(path, "utf-8"));
39605
+ return JSON.parse(readFileSync20(path, "utf-8"));
39427
39606
  } catch {}
39428
39607
  }
39429
39608
  return { version: 1, schedules: [] };
@@ -39646,7 +39825,7 @@ function revisionIdOfRecord(record7) {
39646
39825
  return revisionIdOf(record7);
39647
39826
  }
39648
39827
  // src/lib/pull.ts
39649
- import { existsSync as existsSync17, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readFileSync as readFileSync20, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as writeFileSync11 } from "fs";
39828
+ import { existsSync as existsSync17, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readFileSync as readFileSync21, renameSync as renameSync6, rmSync as rmSync5, writeFileSync as writeFileSync11 } from "fs";
39650
39829
  import { dirname as dirname19, join as join32 } from "path";
39651
39830
 
39652
39831
  // src/lib/skill-bundles.ts
@@ -39846,7 +40025,7 @@ function reconcileTombstone(slug2, corpusOptions) {
39846
40025
  }
39847
40026
  function readPullMarker(dir) {
39848
40027
  try {
39849
- return JSON.parse(readFileSync20(join32(dir, PULL_MARKER_FILE), "utf-8"));
40028
+ return JSON.parse(readFileSync21(join32(dir, PULL_MARKER_FILE), "utf-8"));
39850
40029
  } catch {
39851
40030
  return null;
39852
40031
  }
@@ -42098,7 +42277,7 @@ import { createHash as createHash21, createHmac as createHmac2 } from "crypto";
42098
42277
  import {
42099
42278
  existsSync as existsSync19,
42100
42279
  mkdirSync as mkdirSync15,
42101
- readFileSync as readFileSync21,
42280
+ readFileSync as readFileSync23,
42102
42281
  readdirSync as readdirSync13,
42103
42282
  statSync as statSync11,
42104
42283
  writeFileSync as writeFileSync13
@@ -42265,7 +42444,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
42265
42444
  const files = [];
42266
42445
  if (existsSync19(projectStateDir)) {
42267
42446
  for (const filePath of walkFiles2(projectStateDir)) {
42268
- const bytes = readFileSync21(filePath);
42447
+ const bytes = readFileSync23(filePath);
42269
42448
  const relativePath = toPosix(relative8(targetDir, filePath));
42270
42449
  files.push({
42271
42450
  path: relativePath,
@@ -42683,7 +42862,7 @@ import { createHash as createHash22 } from "crypto";
42683
42862
  import {
42684
42863
  copyFileSync,
42685
42864
  mkdirSync as mkdirSync16,
42686
- readFileSync as readFileSync23,
42865
+ readFileSync as readFileSync25,
42687
42866
  statSync as statSync13,
42688
42867
  writeFileSync as writeFileSync14
42689
42868
  } from "fs";
@@ -42854,7 +43033,7 @@ function validateStationId(stationId) {
42854
43033
  }
42855
43034
  }
42856
43035
  function sha256File(filePath) {
42857
- return createHash22("sha256").update(readFileSync23(filePath)).digest("hex");
43036
+ return createHash22("sha256").update(readFileSync25(filePath)).digest("hex");
42858
43037
  }
42859
43038
  function scanHome(definition, homesRoot) {
42860
43039
  const homePath = homePathFor(definition, homesRoot);
@@ -43010,7 +43189,7 @@ import {
43010
43189
  copyFileSync as copyFileSync2,
43011
43190
  mkdirSync as mkdirSync17,
43012
43191
  readdirSync as readdirSync15,
43013
- readFileSync as readFileSync25,
43192
+ readFileSync as readFileSync26,
43014
43193
  statSync as statSync14,
43015
43194
  writeFileSync as writeFileSync15
43016
43195
  } from "fs";
@@ -43029,7 +43208,7 @@ function readSnapshotManifest(repoRoot, stationId) {
43029
43208
  const manifestPath = join36(snapshotRoot, "sync-manifest.json");
43030
43209
  let manifest;
43031
43210
  try {
43032
- manifest = JSON.parse(readFileSync25(manifestPath, "utf8"));
43211
+ manifest = JSON.parse(readFileSync26(manifestPath, "utf8"));
43033
43212
  } catch (error61) {
43034
43213
  fail("MANIFEST_UNREADABLE", `cannot read snapshot manifest: ${manifestPath}: ${error61.message}`);
43035
43214
  }
@@ -43165,7 +43344,7 @@ function planStationHydration(stationId, repoRoot) {
43165
43344
  for (const copy of copies) {
43166
43345
  let isStub = false;
43167
43346
  try {
43168
- isStub = isPointerSkillMd(readFileSync25(copy.fullPath, "utf8"));
43347
+ isStub = isPointerSkillMd(readFileSync26(copy.fullPath, "utf8"));
43169
43348
  } catch {
43170
43349
  isStub = false;
43171
43350
  }