@delorenj/skillex 0.4.0 → 0.4.1

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.
Files changed (3) hide show
  1. package/dist/cli.js +67 -28
  2. package/dist/index.js +67 -28
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -8620,7 +8620,7 @@ async function buildScope(scope, options, forbiddenRoots, verifySet = verifySele
8620
8620
  if (!owned4)
8621
8621
  refuse3(root, "Whole-root switching requires explicit ownership of the existing root.");
8622
8622
  if (owned4.kind === "link") return { identity: owned4 };
8623
- const children = {};
8623
+ const children2 = {};
8624
8624
  for (const name of awaitableNames) {
8625
8625
  const child = links[join25(root, name)];
8626
8626
  if (!child)
@@ -8628,9 +8628,9 @@ async function buildScope(scope, options, forbiddenRoots, verifySet = verifySele
8628
8628
  join25(root, name),
8629
8629
  "Foreign content prevents a whole-root switch; it will not be removed."
8630
8630
  );
8631
- children[name] = child;
8631
+ children2[name] = child;
8632
8632
  }
8633
- return { identity: owned4, children };
8633
+ return { identity: owned4, children: children2 };
8634
8634
  };
8635
8635
  const awaitableNames = rootEntry?.info.isDirectory() ? await readdir16(physicalPath(root, recovery)) : [];
8636
8636
  if (!rootEntry) {
@@ -8969,7 +8969,7 @@ async function publishPrepared(work, staged, destination, node, directories) {
8969
8969
  async function stageNode(work, operation2, path) {
8970
8970
  if (operation2.children !== void 0) {
8971
8971
  await mkdir7(path, { mode: 493 });
8972
- const children = {};
8972
+ const children2 = {};
8973
8973
  for (const [name, target2] of operation2.children) {
8974
8974
  const destination = join25(operation2.path, name);
8975
8975
  if (!await canonicalTarget(work, name, target2))
@@ -8978,11 +8978,11 @@ async function stageNode(work, operation2, path) {
8978
8978
  await symlink4(relative9(dirname13(destination), target2), child);
8979
8979
  const found2 = await entry2(child);
8980
8980
  if (!found2) refuse3(child, "Prepared activation link disappeared.");
8981
- children[name] = identity(found2);
8981
+ children2[name] = identity(found2);
8982
8982
  }
8983
8983
  const found = await entry2(path);
8984
8984
  if (!found) refuse3(path, "Prepared activation directory disappeared.");
8985
- return { identity: identity(found), children };
8985
+ return { identity: identity(found), children: children2 };
8986
8986
  }
8987
8987
  if (operation2.target !== void 0) {
8988
8988
  if (dirname13(operation2.path) === work.plan.activationRoot && !await canonicalTarget(work, basename5(operation2.path), operation2.target))
@@ -11417,7 +11417,7 @@ async function captureMigrationNode(path) {
11417
11417
  mode: Number(before.mode & 0o7777n),
11418
11418
  ...before.isSymbolicLink() ? { raw: await readlink6(path) } : {}
11419
11419
  };
11420
- const children = {};
11420
+ const children2 = {};
11421
11421
  let hash5 = null;
11422
11422
  if (before.isDirectory()) {
11423
11423
  const names2 = await readdir22(path, { encoding: "buffer" });
@@ -11434,7 +11434,7 @@ async function captureMigrationNode(path) {
11434
11434
  const child = await captureMigrationNode(join31(path, name));
11435
11435
  if (!child)
11436
11436
  migrationRefusal(join31(path, name), "Content disappeared during migration inventory.");
11437
- Object.defineProperty(children, name, { value: child, enumerable: true });
11437
+ Object.defineProperty(children2, name, { value: child, enumerable: true });
11438
11438
  }
11439
11439
  } else if (before.isFile()) {
11440
11440
  const handle = await open10(
@@ -11473,7 +11473,7 @@ async function captureMigrationNode(path) {
11473
11473
  const after = await lstat20(path, { bigint: true });
11474
11474
  if (after.dev !== before.dev || after.ino !== before.ino || after.mode !== before.mode || after.size !== before.size || after.mtimeNs !== before.mtimeNs || after.ctimeNs !== before.ctimeNs)
11475
11475
  migrationRefusal(path, "Content changed while its migration inventory was being captured.");
11476
- return { identity: identity5, hash: hash5, children };
11476
+ return { identity: identity5, hash: hash5, children: children2 };
11477
11477
  }
11478
11478
  function migrationDigest(node) {
11479
11479
  const project = (item2) => ({
@@ -12574,16 +12574,16 @@ async function prepare2(target2) {
12574
12574
  shared,
12575
12575
  "The old alias points at one skill definition rather than a collection root."
12576
12576
  );
12577
- const children = {};
12577
+ const children2 = {};
12578
12578
  const claimNames = [];
12579
12579
  const proofs = [{ path: shared, node: contents }];
12580
12580
  for (const name of Object.keys(contents.children)) {
12581
12581
  const path = join34(target2.root, name);
12582
12582
  const mapped2 = await canonicalMapping(target2, path);
12583
12583
  const output2 = mapped2?.name ?? name;
12584
- if (Object.hasOwn(children, output2))
12584
+ if (Object.hasOwn(children2, output2))
12585
12585
  migrationRefusal(path, "Two original children map to the same resulting name.");
12586
- Object.defineProperty(children, output2, {
12586
+ Object.defineProperty(children2, output2, {
12587
12587
  value: mapped2?.target ?? join34(shared, name),
12588
12588
  enumerable: true
12589
12589
  });
@@ -12598,7 +12598,7 @@ async function prepare2(target2) {
12598
12598
  [
12599
12599
  mapped2 ? "The mapped canonical content is verified." : "The original child remains reachable through a foreign forwarding link; its shared target is untouched."
12600
12600
  ],
12601
- children[output2]
12601
+ children2[output2]
12602
12602
  )
12603
12603
  );
12604
12604
  }
@@ -12607,7 +12607,7 @@ async function prepare2(target2) {
12607
12607
  "Replace only the old root symlink with a real directory; preserve every inventoried child and leave the shared target untouched."
12608
12608
  ]),
12609
12609
  previous: root,
12610
- children,
12610
+ children: children2,
12611
12611
  claimRoot: true,
12612
12612
  claimNames,
12613
12613
  proofs
@@ -18229,13 +18229,13 @@ async function normalizeScope(options = {}) {
18229
18229
  // src/core/profile-sync.ts
18230
18230
  import { randomUUID as randomUUID11 } from "node:crypto";
18231
18231
  import { constants as constants13 } from "node:fs";
18232
- import { mkdir as mkdir12, open as open17, readdir as readdir29, realpath as realpath32, rename as rename11, symlink as symlink7, unlink as unlink12 } from "node:fs/promises";
18232
+ import { mkdir as mkdir12, open as open17, readdir as readdir30, realpath as realpath32, rename as rename11, symlink as symlink7, unlink as unlink12 } from "node:fs/promises";
18233
18233
  import { dirname as dirname27, join as join44, resolve as resolve25 } from "node:path";
18234
18234
  import { isDeepStrictEqual as isDeepStrictEqual10 } from "node:util";
18235
18235
 
18236
18236
  // src/core/profile-policy.ts
18237
18237
  import { constants as constants12 } from "node:fs";
18238
- import { open as open16, readFile as readFile18, stat as stat8 } from "node:fs/promises";
18238
+ import { open as open16, readdir as readdir29, readFile as readFile18, stat as stat8 } from "node:fs/promises";
18239
18239
  import { join as join43 } from "node:path";
18240
18240
  import { parseDocument as parseDocument4 } from "yaml";
18241
18241
  var metadataFiles = /* @__PURE__ */ new Set([
@@ -18248,9 +18248,48 @@ var metadataFiles = /* @__PURE__ */ new Set([
18248
18248
  ".curator_ledger.jsonl"
18249
18249
  ]);
18250
18250
  var metadataDirectories = /* @__PURE__ */ new Set([".curator_backups", ".locks"]);
18251
- function hermesMetadata(item2) {
18251
+ var hubFiles = /* @__PURE__ */ new Set(["audit.log", "taps.json", "lock.json"]);
18252
+ async function children(path) {
18253
+ try {
18254
+ return await readdir29(path, { withFileTypes: true });
18255
+ } catch {
18256
+ return null;
18257
+ }
18258
+ }
18259
+ async function categoryStub(item2) {
18260
+ if (item2.kind !== "directory" || item2.shadows) return false;
18261
+ const entries = await children(item2.path);
18262
+ return entries?.length === 1 && entries[0]?.name === "DESCRIPTION.md" && entries[0].isFile();
18263
+ }
18264
+ async function idleHub(item2) {
18265
+ if (item2.kind !== "directory" || item2.name !== ".hub") return false;
18266
+ const entries = await children(item2.path);
18267
+ if (!entries) return false;
18268
+ for (const child of entries) {
18269
+ const path = join43(item2.path, child.name);
18270
+ if (child.isFile() && hubFiles.has(child.name)) {
18271
+ if (child.name !== "lock.json") continue;
18272
+ try {
18273
+ const lock = JSON.parse(await readFile18(path, "utf8"));
18274
+ const installed = lock?.installed;
18275
+ if (!installed || typeof installed !== "object" || Object.keys(installed).length)
18276
+ return false;
18277
+ } catch {
18278
+ return false;
18279
+ }
18280
+ } else if (child.isDirectory() && child.name === "quarantine") {
18281
+ if ((await children(path))?.length !== 0) return false;
18282
+ } else if (child.isDirectory() && child.name === "index-cache") {
18283
+ const cached2 = await children(path);
18284
+ if (!cached2 || cached2.some((entry3) => !entry3.isFile())) return false;
18285
+ } else return false;
18286
+ }
18287
+ return true;
18288
+ }
18289
+ async function hermesMetadata(item2) {
18252
18290
  if (item2.kind === "file") return metadataFiles.has(item2.name);
18253
- if (item2.kind === "directory") return metadataDirectories.has(item2.name);
18291
+ if (item2.kind === "directory")
18292
+ return metadataDirectories.has(item2.name) || await idleHub(item2) || await categoryStub(item2);
18254
18293
  return false;
18255
18294
  }
18256
18295
  function refuse5(path, message) {
@@ -18288,7 +18327,7 @@ async function profileInheritsGlobal(profile) {
18288
18327
  async function assertStrictProfile(profile, options, local) {
18289
18328
  if (!await strictProfile(profile, options)) return false;
18290
18329
  for (const item2 of local)
18291
- if (!hermesMetadata(item2))
18330
+ if (!await hermesMetadata(item2))
18292
18331
  refuse5(item2.path, `Skillex-only profile contains unowned entry ${item2.name}.`);
18293
18332
  const path = join43(profile.root, "config.yaml");
18294
18333
  const config = await entry2(path);
@@ -18461,7 +18500,7 @@ async function observations(profile, state) {
18461
18500
  "Use the explicit migrate workflow to replace this whole-root alias or conflicting file while preserving profile content."
18462
18501
  );
18463
18502
  if (root)
18464
- for (const name of (await readdir29(profile.skillsRoot)).sort()) {
18503
+ for (const name of (await readdir30(profile.skillsRoot)).sort()) {
18465
18504
  const path = join44(profile.skillsRoot, name);
18466
18505
  const actual = await entry2(path);
18467
18506
  if (!actual) continue;
@@ -20669,7 +20708,7 @@ async function adoptRoot(options = {}) {
20669
20708
  }
20670
20709
 
20671
20710
  // src/core/sets.ts
20672
- import { readdir as readdir30 } from "node:fs/promises";
20711
+ import { readdir as readdir31 } from "node:fs/promises";
20673
20712
  import { join as join47 } from "node:path";
20674
20713
  function validateName(name) {
20675
20714
  parseManifest({ sets: [{ name }] }, "set name");
@@ -20692,7 +20731,7 @@ async function listSets(options = {}) {
20692
20731
  const failures = [];
20693
20732
  if (await inspectPath(path)) {
20694
20733
  await requireDirectory(path, "E_SET_MISSING");
20695
- for (const entry3 of (await readdir30(path, { withFileTypes: true })).sort(
20734
+ for (const entry3 of (await readdir31(path, { withFileTypes: true })).sort(
20696
20735
  (a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0
20697
20736
  )) {
20698
20737
  if (entry3.name.startsWith(".") || entry3.name.startsWith("_") || !entry3.isDirectory() && !entry3.isSymbolicLink())
@@ -20834,7 +20873,7 @@ async function removeSetSkills(name, names2, options = {}) {
20834
20873
  }
20835
20874
 
20836
20875
  // src/core/vendor-inspect.ts
20837
- import { readdir as readdir31 } from "node:fs/promises";
20876
+ import { readdir as readdir32 } from "node:fs/promises";
20838
20877
  import { join as join48 } from "node:path";
20839
20878
 
20840
20879
  // src/core/vendor-git.ts
@@ -21126,8 +21165,8 @@ async function readVendorGitSource(source, checkoutRoot, options = {}) {
21126
21165
  checkout
21127
21166
  ).trim();
21128
21167
  const base = await treeAt(checkout, rootTree, source.subdir, options);
21129
- const children = await treeEntries(checkout, base, options);
21130
- const declarations = source.membership === "explicit" ? source.skills : children.filter(
21168
+ const children2 = await treeEntries(checkout, base, options);
21169
+ const declarations = source.membership === "explicit" ? source.skills : children2.filter(
21131
21170
  (entry3) => !entry3.name.startsWith(".") && !entry3.name.startsWith("_") && (entry3.type === "tree" || entry3.mode === "120000" || entry3.mode === "160000")
21132
21171
  ).map((entry3) => ({ name: entry3.name, dir: entry3.name }));
21133
21172
  const selected = declarations.filter(
@@ -21199,7 +21238,7 @@ async function recordedInventory(manifest, observation, options) {
21199
21238
  const recorded = /* @__PURE__ */ new Map();
21200
21239
  const failed2 = /* @__PURE__ */ new Set();
21201
21240
  const catalog = join48(manifest.registry.root, "all-skills");
21202
- for (const entry3 of (await readdir31(catalog, { withFileTypes: true })).sort(
21241
+ for (const entry3 of (await readdir32(catalog, { withFileTypes: true })).sort(
21203
21242
  (a, b) => a.name.localeCompare(b.name)
21204
21243
  )) {
21205
21244
  checkVendorSignal(options);
@@ -21672,7 +21711,7 @@ async function inspectVendorStatus(options = {}) {
21672
21711
 
21673
21712
  // src/core/vendor-sync.ts
21674
21713
  import { randomUUID as randomUUID14 } from "node:crypto";
21675
- import { chmod as chmod4, lstat as lstat27, mkdir as mkdir14, open as open20, readdir as readdir32, rename as rename14 } from "node:fs/promises";
21714
+ import { chmod as chmod4, lstat as lstat27, mkdir as mkdir14, open as open20, readdir as readdir33, rename as rename14 } from "node:fs/promises";
21676
21715
  import { dirname as dirname30, join as join49 } from "node:path";
21677
21716
  function refuse8(code, message, path, fix) {
21678
21717
  fail(code, message, { path, fix }, ExitCode.REFUSED);
@@ -21899,7 +21938,7 @@ async function prepare6(options, extractedAt) {
21899
21938
  }
21900
21939
  }
21901
21940
  if (options.prune) {
21902
- for (const name of (await readdir32(catalog)).filter(isSkillName).sort()) {
21941
+ for (const name of (await readdir33(catalog)).filter(isSkillName).sort()) {
21903
21942
  if (names2.has(name)) continue;
21904
21943
  const path = join49(catalog, name);
21905
21944
  if (!(await vendorLstat(path))?.isDirectory()) continue;
package/dist/index.js CHANGED
@@ -8618,7 +8618,7 @@ async function buildScope(scope, options, forbiddenRoots, verifySet = verifySele
8618
8618
  if (!owned4)
8619
8619
  refuse3(root, "Whole-root switching requires explicit ownership of the existing root.");
8620
8620
  if (owned4.kind === "link") return { identity: owned4 };
8621
- const children = {};
8621
+ const children2 = {};
8622
8622
  for (const name of awaitableNames) {
8623
8623
  const child = links[join25(root, name)];
8624
8624
  if (!child)
@@ -8626,9 +8626,9 @@ async function buildScope(scope, options, forbiddenRoots, verifySet = verifySele
8626
8626
  join25(root, name),
8627
8627
  "Foreign content prevents a whole-root switch; it will not be removed."
8628
8628
  );
8629
- children[name] = child;
8629
+ children2[name] = child;
8630
8630
  }
8631
- return { identity: owned4, children };
8631
+ return { identity: owned4, children: children2 };
8632
8632
  };
8633
8633
  const awaitableNames = rootEntry?.info.isDirectory() ? await readdir16(physicalPath(root, recovery)) : [];
8634
8634
  if (!rootEntry) {
@@ -8967,7 +8967,7 @@ async function publishPrepared(work, staged, destination, node, directories) {
8967
8967
  async function stageNode(work, operation2, path) {
8968
8968
  if (operation2.children !== void 0) {
8969
8969
  await mkdir7(path, { mode: 493 });
8970
- const children = {};
8970
+ const children2 = {};
8971
8971
  for (const [name, target2] of operation2.children) {
8972
8972
  const destination = join25(operation2.path, name);
8973
8973
  if (!await canonicalTarget(work, name, target2))
@@ -8976,11 +8976,11 @@ async function stageNode(work, operation2, path) {
8976
8976
  await symlink4(relative9(dirname13(destination), target2), child);
8977
8977
  const found2 = await entry2(child);
8978
8978
  if (!found2) refuse3(child, "Prepared activation link disappeared.");
8979
- children[name] = identity(found2);
8979
+ children2[name] = identity(found2);
8980
8980
  }
8981
8981
  const found = await entry2(path);
8982
8982
  if (!found) refuse3(path, "Prepared activation directory disappeared.");
8983
- return { identity: identity(found), children };
8983
+ return { identity: identity(found), children: children2 };
8984
8984
  }
8985
8985
  if (operation2.target !== void 0) {
8986
8986
  if (dirname13(operation2.path) === work.plan.activationRoot && !await canonicalTarget(work, basename5(operation2.path), operation2.target))
@@ -11415,7 +11415,7 @@ async function captureMigrationNode(path) {
11415
11415
  mode: Number(before.mode & 0o7777n),
11416
11416
  ...before.isSymbolicLink() ? { raw: await readlink6(path) } : {}
11417
11417
  };
11418
- const children = {};
11418
+ const children2 = {};
11419
11419
  let hash5 = null;
11420
11420
  if (before.isDirectory()) {
11421
11421
  const names2 = await readdir22(path, { encoding: "buffer" });
@@ -11432,7 +11432,7 @@ async function captureMigrationNode(path) {
11432
11432
  const child = await captureMigrationNode(join31(path, name));
11433
11433
  if (!child)
11434
11434
  migrationRefusal(join31(path, name), "Content disappeared during migration inventory.");
11435
- Object.defineProperty(children, name, { value: child, enumerable: true });
11435
+ Object.defineProperty(children2, name, { value: child, enumerable: true });
11436
11436
  }
11437
11437
  } else if (before.isFile()) {
11438
11438
  const handle = await open10(
@@ -11471,7 +11471,7 @@ async function captureMigrationNode(path) {
11471
11471
  const after = await lstat20(path, { bigint: true });
11472
11472
  if (after.dev !== before.dev || after.ino !== before.ino || after.mode !== before.mode || after.size !== before.size || after.mtimeNs !== before.mtimeNs || after.ctimeNs !== before.ctimeNs)
11473
11473
  migrationRefusal(path, "Content changed while its migration inventory was being captured.");
11474
- return { identity: identity4, hash: hash5, children };
11474
+ return { identity: identity4, hash: hash5, children: children2 };
11475
11475
  }
11476
11476
  function migrationDigest(node) {
11477
11477
  const project = (item2) => ({
@@ -12572,16 +12572,16 @@ async function prepare2(target2) {
12572
12572
  shared,
12573
12573
  "The old alias points at one skill definition rather than a collection root."
12574
12574
  );
12575
- const children = {};
12575
+ const children2 = {};
12576
12576
  const claimNames = [];
12577
12577
  const proofs = [{ path: shared, node: contents }];
12578
12578
  for (const name of Object.keys(contents.children)) {
12579
12579
  const path = join34(target2.root, name);
12580
12580
  const mapped2 = await canonicalMapping(target2, path);
12581
12581
  const output = mapped2?.name ?? name;
12582
- if (Object.hasOwn(children, output))
12582
+ if (Object.hasOwn(children2, output))
12583
12583
  migrationRefusal(path, "Two original children map to the same resulting name.");
12584
- Object.defineProperty(children, output, {
12584
+ Object.defineProperty(children2, output, {
12585
12585
  value: mapped2?.target ?? join34(shared, name),
12586
12586
  enumerable: true
12587
12587
  });
@@ -12596,7 +12596,7 @@ async function prepare2(target2) {
12596
12596
  [
12597
12597
  mapped2 ? "The mapped canonical content is verified." : "The original child remains reachable through a foreign forwarding link; its shared target is untouched."
12598
12598
  ],
12599
- children[output]
12599
+ children2[output]
12600
12600
  )
12601
12601
  );
12602
12602
  }
@@ -12605,7 +12605,7 @@ async function prepare2(target2) {
12605
12605
  "Replace only the old root symlink with a real directory; preserve every inventoried child and leave the shared target untouched."
12606
12606
  ]),
12607
12607
  previous: root,
12608
- children,
12608
+ children: children2,
12609
12609
  claimRoot: true,
12610
12610
  claimNames,
12611
12611
  proofs
@@ -18227,13 +18227,13 @@ async function normalizeScope(options = {}) {
18227
18227
  // src/core/profile-sync.ts
18228
18228
  import { randomUUID as randomUUID11 } from "node:crypto";
18229
18229
  import { constants as constants13 } from "node:fs";
18230
- import { mkdir as mkdir12, open as open17, readdir as readdir29, realpath as realpath32, rename as rename11, symlink as symlink7, unlink as unlink12 } from "node:fs/promises";
18230
+ import { mkdir as mkdir12, open as open17, readdir as readdir30, realpath as realpath32, rename as rename11, symlink as symlink7, unlink as unlink12 } from "node:fs/promises";
18231
18231
  import { dirname as dirname27, join as join44, resolve as resolve25 } from "node:path";
18232
18232
  import { isDeepStrictEqual as isDeepStrictEqual10 } from "node:util";
18233
18233
 
18234
18234
  // src/core/profile-policy.ts
18235
18235
  import { constants as constants12 } from "node:fs";
18236
- import { open as open16, readFile as readFile18, stat as stat8 } from "node:fs/promises";
18236
+ import { open as open16, readdir as readdir29, readFile as readFile18, stat as stat8 } from "node:fs/promises";
18237
18237
  import { join as join43 } from "node:path";
18238
18238
  import { parseDocument as parseDocument4 } from "yaml";
18239
18239
  var metadataFiles = /* @__PURE__ */ new Set([
@@ -18246,9 +18246,48 @@ var metadataFiles = /* @__PURE__ */ new Set([
18246
18246
  ".curator_ledger.jsonl"
18247
18247
  ]);
18248
18248
  var metadataDirectories = /* @__PURE__ */ new Set([".curator_backups", ".locks"]);
18249
- function hermesMetadata(item2) {
18249
+ var hubFiles = /* @__PURE__ */ new Set(["audit.log", "taps.json", "lock.json"]);
18250
+ async function children(path) {
18251
+ try {
18252
+ return await readdir29(path, { withFileTypes: true });
18253
+ } catch {
18254
+ return null;
18255
+ }
18256
+ }
18257
+ async function categoryStub(item2) {
18258
+ if (item2.kind !== "directory" || item2.shadows) return false;
18259
+ const entries = await children(item2.path);
18260
+ return entries?.length === 1 && entries[0]?.name === "DESCRIPTION.md" && entries[0].isFile();
18261
+ }
18262
+ async function idleHub(item2) {
18263
+ if (item2.kind !== "directory" || item2.name !== ".hub") return false;
18264
+ const entries = await children(item2.path);
18265
+ if (!entries) return false;
18266
+ for (const child of entries) {
18267
+ const path = join43(item2.path, child.name);
18268
+ if (child.isFile() && hubFiles.has(child.name)) {
18269
+ if (child.name !== "lock.json") continue;
18270
+ try {
18271
+ const lock = JSON.parse(await readFile18(path, "utf8"));
18272
+ const installed = lock?.installed;
18273
+ if (!installed || typeof installed !== "object" || Object.keys(installed).length)
18274
+ return false;
18275
+ } catch {
18276
+ return false;
18277
+ }
18278
+ } else if (child.isDirectory() && child.name === "quarantine") {
18279
+ if ((await children(path))?.length !== 0) return false;
18280
+ } else if (child.isDirectory() && child.name === "index-cache") {
18281
+ const cached2 = await children(path);
18282
+ if (!cached2 || cached2.some((entry3) => !entry3.isFile())) return false;
18283
+ } else return false;
18284
+ }
18285
+ return true;
18286
+ }
18287
+ async function hermesMetadata(item2) {
18250
18288
  if (item2.kind === "file") return metadataFiles.has(item2.name);
18251
- if (item2.kind === "directory") return metadataDirectories.has(item2.name);
18289
+ if (item2.kind === "directory")
18290
+ return metadataDirectories.has(item2.name) || await idleHub(item2) || await categoryStub(item2);
18252
18291
  return false;
18253
18292
  }
18254
18293
  function refuse5(path, message) {
@@ -18286,7 +18325,7 @@ async function profileInheritsGlobal(profile) {
18286
18325
  async function assertStrictProfile(profile, options, local) {
18287
18326
  if (!await strictProfile(profile, options)) return false;
18288
18327
  for (const item2 of local)
18289
- if (!hermesMetadata(item2))
18328
+ if (!await hermesMetadata(item2))
18290
18329
  refuse5(item2.path, `Skillex-only profile contains unowned entry ${item2.name}.`);
18291
18330
  const path = join43(profile.root, "config.yaml");
18292
18331
  const config = await entry2(path);
@@ -18459,7 +18498,7 @@ async function observations(profile, state) {
18459
18498
  "Use the explicit migrate workflow to replace this whole-root alias or conflicting file while preserving profile content."
18460
18499
  );
18461
18500
  if (root)
18462
- for (const name of (await readdir29(profile.skillsRoot)).sort()) {
18501
+ for (const name of (await readdir30(profile.skillsRoot)).sort()) {
18463
18502
  const path = join44(profile.skillsRoot, name);
18464
18503
  const actual = await entry2(path);
18465
18504
  if (!actual) continue;
@@ -20667,7 +20706,7 @@ async function adoptRoot(options = {}) {
20667
20706
  }
20668
20707
 
20669
20708
  // src/core/sets.ts
20670
- import { readdir as readdir30 } from "node:fs/promises";
20709
+ import { readdir as readdir31 } from "node:fs/promises";
20671
20710
  import { join as join47 } from "node:path";
20672
20711
  function validateName(name) {
20673
20712
  parseManifest({ sets: [{ name }] }, "set name");
@@ -20690,7 +20729,7 @@ async function listSets(options = {}) {
20690
20729
  const failures = [];
20691
20730
  if (await inspectPath(path)) {
20692
20731
  await requireDirectory(path, "E_SET_MISSING");
20693
- for (const entry3 of (await readdir30(path, { withFileTypes: true })).sort(
20732
+ for (const entry3 of (await readdir31(path, { withFileTypes: true })).sort(
20694
20733
  (a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0
20695
20734
  )) {
20696
20735
  if (entry3.name.startsWith(".") || entry3.name.startsWith("_") || !entry3.isDirectory() && !entry3.isSymbolicLink())
@@ -20832,7 +20871,7 @@ async function removeSetSkills(name, names2, options = {}) {
20832
20871
  }
20833
20872
 
20834
20873
  // src/core/vendor-inspect.ts
20835
- import { readdir as readdir31 } from "node:fs/promises";
20874
+ import { readdir as readdir32 } from "node:fs/promises";
20836
20875
  import { join as join48 } from "node:path";
20837
20876
 
20838
20877
  // src/core/vendor-git.ts
@@ -21124,8 +21163,8 @@ async function readVendorGitSource(source, checkoutRoot, options = {}) {
21124
21163
  checkout
21125
21164
  ).trim();
21126
21165
  const base = await treeAt(checkout, rootTree, source.subdir, options);
21127
- const children = await treeEntries(checkout, base, options);
21128
- const declarations = source.membership === "explicit" ? source.skills : children.filter(
21166
+ const children2 = await treeEntries(checkout, base, options);
21167
+ const declarations = source.membership === "explicit" ? source.skills : children2.filter(
21129
21168
  (entry3) => !entry3.name.startsWith(".") && !entry3.name.startsWith("_") && (entry3.type === "tree" || entry3.mode === "120000" || entry3.mode === "160000")
21130
21169
  ).map((entry3) => ({ name: entry3.name, dir: entry3.name }));
21131
21170
  const selected = declarations.filter(
@@ -21197,7 +21236,7 @@ async function recordedInventory(manifest, observation, options) {
21197
21236
  const recorded = /* @__PURE__ */ new Map();
21198
21237
  const failed2 = /* @__PURE__ */ new Set();
21199
21238
  const catalog = join48(manifest.registry.root, "all-skills");
21200
- for (const entry3 of (await readdir31(catalog, { withFileTypes: true })).sort(
21239
+ for (const entry3 of (await readdir32(catalog, { withFileTypes: true })).sort(
21201
21240
  (a, b) => a.name.localeCompare(b.name)
21202
21241
  )) {
21203
21242
  checkVendorSignal(options);
@@ -21670,7 +21709,7 @@ async function inspectVendorStatus(options = {}) {
21670
21709
 
21671
21710
  // src/core/vendor-sync.ts
21672
21711
  import { randomUUID as randomUUID14 } from "node:crypto";
21673
- import { chmod as chmod4, lstat as lstat27, mkdir as mkdir14, open as open20, readdir as readdir32, rename as rename14 } from "node:fs/promises";
21712
+ import { chmod as chmod4, lstat as lstat27, mkdir as mkdir14, open as open20, readdir as readdir33, rename as rename14 } from "node:fs/promises";
21674
21713
  import { dirname as dirname30, join as join49 } from "node:path";
21675
21714
  function refuse8(code, message, path, fix) {
21676
21715
  fail(code, message, { path, fix }, ExitCode.REFUSED);
@@ -21897,7 +21936,7 @@ async function prepare6(options, extractedAt) {
21897
21936
  }
21898
21937
  }
21899
21938
  if (options.prune) {
21900
- for (const name of (await readdir32(catalog)).filter(isSkillName).sort()) {
21939
+ for (const name of (await readdir33(catalog)).filter(isSkillName).sort()) {
21901
21940
  if (names2.has(name)) continue;
21902
21941
  const path = join49(catalog, name);
21903
21942
  if (!(await vendorLstat(path))?.isDirectory()) continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delorenj/skillex",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Canonical skill catalog and activation management for agentic CLIs.",
5
5
  "type": "module",
6
6
  "license": "MIT",