@hasna/skills 0.10.18 → 0.10.20

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/sdk/index.js CHANGED
@@ -25954,12 +25954,12 @@ import fs from "fs/promises";
25954
25954
  var import_client9, import_config17, import_node_http_handler, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", DEFAULT_LINK_LOCAL_HOST = "http://169.254.170.2", AWS_CONTAINER_CREDENTIALS_FULL_URI = "AWS_CONTAINER_CREDENTIALS_FULL_URI", AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE = "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE", AWS_CONTAINER_AUTHORIZATION_TOKEN = "AWS_CONTAINER_AUTHORIZATION_TOKEN", fromHttp = (options = {}) => {
25955
25955
  options.logger?.debug("@aws-sdk/credential-provider-http - fromHttp");
25956
25956
  let host;
25957
- const relative6 = options.awsContainerCredentialsRelativeUri ?? process.env[AWS_CONTAINER_CREDENTIALS_RELATIVE_URI];
25957
+ const relative7 = options.awsContainerCredentialsRelativeUri ?? process.env[AWS_CONTAINER_CREDENTIALS_RELATIVE_URI];
25958
25958
  const full = options.awsContainerCredentialsFullUri ?? process.env[AWS_CONTAINER_CREDENTIALS_FULL_URI];
25959
25959
  const token = options.awsContainerAuthorizationToken ?? process.env[AWS_CONTAINER_AUTHORIZATION_TOKEN];
25960
25960
  const tokenFile = options.awsContainerAuthorizationTokenFile ?? process.env[AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE];
25961
25961
  const warn = options.logger?.constructor?.name === "NoOpLogger" || !options.logger?.warn ? console.warn : options.logger.warn.bind(options.logger);
25962
- if (relative6 && full) {
25962
+ if (relative7 && full) {
25963
25963
  warn("@aws-sdk/credential-provider-http: " + "you have set both awsContainerCredentialsRelativeUri and awsContainerCredentialsFullUri.");
25964
25964
  warn("awsContainerCredentialsRelativeUri will take precedence.");
25965
25965
  }
@@ -25967,8 +25967,8 @@ var import_client9, import_config17, import_node_http_handler, AWS_CONTAINER_CRE
25967
25967
  warn("@aws-sdk/credential-provider-http: " + "you have set both awsContainerAuthorizationToken and awsContainerAuthorizationTokenFile.");
25968
25968
  warn("awsContainerAuthorizationTokenFile will take precedence.");
25969
25969
  }
25970
- if (relative6) {
25971
- host = `${DEFAULT_LINK_LOCAL_HOST}${relative6}`;
25970
+ if (relative7) {
25971
+ host = `${DEFAULT_LINK_LOCAL_HOST}${relative7}`;
25972
25972
  } else if (full) {
25973
25973
  host = full;
25974
25974
  } else {
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
32373
32373
  // package.json
32374
32374
  var package_default = {
32375
32375
  name: "@hasna/skills",
32376
- version: "0.10.18",
32376
+ version: "0.10.20",
32377
32377
  description: "Skills library for AI coding agents",
32378
32378
  type: "module",
32379
32379
  bin: {
@@ -37692,7 +37692,7 @@ import {
37692
37692
  writeFileSync as writeFileSync7
37693
37693
  } from "fs";
37694
37694
  import { homedir as homedir5 } from "os";
37695
- import { basename as basename10, dirname as dirname19, join as join27 } from "path";
37695
+ import { basename as basename11, dirname as dirname19, join as join27 } from "path";
37696
37696
 
37697
37697
  // src/lib/claude-settings-witness.ts
37698
37698
  import { createHash as createHash5 } from "crypto";
@@ -38006,7 +38006,7 @@ function hashNativeJsonControls(text, excludedMetadata) {
38006
38006
  }
38007
38007
 
38008
38008
  // src/lib/codex-plugin-skill-controls.ts
38009
- import { dirname as dirname5, join as join10, resolve as resolve4, sep as sep2 } from "path";
38009
+ import { basename as basename3, dirname as dirname5, join as join10, relative as relative3, resolve as resolve4, sep as sep2 } from "path";
38010
38010
  import { lstatSync as lstatSync6, readdirSync as readdirSync3, readlinkSync as readlinkSync2, realpathSync as realpathSync5 } from "fs";
38011
38011
 
38012
38012
  // src/lib/codex-native-skill-catalog.ts
@@ -38160,6 +38160,7 @@ async function connectCodexHookRpc(options) {
38160
38160
  var MAX_SKILLS = 4096;
38161
38161
  var MAX_NAME_BYTES = 1024;
38162
38162
  var MAX_PATH_BYTES = 16384;
38163
+ var MAX_PLUGIN_ID_BYTES = 1024;
38163
38164
  var SUPPORTED_NAME_CONTROL_VERSIONS = new Set(["codex-cli 0.159.2"]);
38164
38165
  function refuse2() {
38165
38166
  throw new Error("CODEX_NATIVE_SKILL_CATALOG_INVALID");
@@ -38183,6 +38184,35 @@ function projectCodexNativeSkillCatalog(response, cwd) {
38183
38184
  return { name: value.name, path: value.path, enabled: value.enabled, pluginId: value.pluginId ?? null };
38184
38185
  });
38185
38186
  }
38187
+ function projectCodexInstalledPlugins(response) {
38188
+ if (!object2(response) || !Array.isArray(response.marketplaces) || !Array.isArray(response.marketplaceLoadErrors) || response.marketplaceLoadErrors.length !== 0 || response.marketplaces.length > MAX_SKILLS)
38189
+ refuse2();
38190
+ const plugins = [];
38191
+ for (const marketplace of response.marketplaces) {
38192
+ if (!object2(marketplace) || !scalar(marketplace.name, MAX_NAME_BYTES) || !Array.isArray(marketplace.plugins) || marketplace.plugins.length > MAX_SKILLS)
38193
+ refuse2();
38194
+ for (const value of marketplace.plugins) {
38195
+ if (!object2(value) || !scalar(value.id, MAX_PLUGIN_ID_BYTES) || !scalar(value.name, MAX_NAME_BYTES) || value.id !== `${value.name}@${marketplace.name}`)
38196
+ refuse2();
38197
+ plugins.push({ ...value, marketplace: marketplace.name });
38198
+ }
38199
+ }
38200
+ return projectCodexInstalledPluginEntries(plugins);
38201
+ }
38202
+ function projectCodexInstalledPluginEntries(value) {
38203
+ if (!Array.isArray(value) || value.length > MAX_SKILLS)
38204
+ refuse2();
38205
+ const ids = new Set;
38206
+ return value.map((item) => {
38207
+ if (!object2(item) || !scalar(item.id, MAX_PLUGIN_ID_BYTES) || !scalar(item.name, MAX_NAME_BYTES) || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean" || item.localVersion !== null && !scalar(item.localVersion, MAX_NAME_BYTES))
38208
+ refuse2();
38209
+ const at = item.id.lastIndexOf("@");
38210
+ if (at < 1 || item.id.slice(0, at) !== item.name || at === item.id.length - 1 || ids.has(item.id))
38211
+ refuse2();
38212
+ ids.add(item.id);
38213
+ return { id: item.id, name: item.name, installed: item.installed, enabled: item.enabled, localVersion: item.localVersion ?? null };
38214
+ });
38215
+ }
38186
38216
  async function captureCodexNativeSkillCatalog(options, connect = connectCodexHookRpc) {
38187
38217
  if (!absolutePath2(options.cwd) || !absolutePath2(options.home))
38188
38218
  refuse2();
@@ -38196,7 +38226,13 @@ async function captureCodexNativeSkillCatalog(options, connect = connectCodexHoo
38196
38226
  if (!SUPPORTED_NAME_CONTROL_VERSIONS.has(rpc.version))
38197
38227
  throw new Error("CODEX_NATIVE_SKILL_CATALOG_UNSUPPORTED_VERSION");
38198
38228
  const response = await rpc.request("skills/list", { cwds: [options.cwd], forceReload: true });
38199
- return { version: rpc.version, cwd: options.cwd, skills: projectCodexNativeSkillCatalog(response, options.cwd) };
38229
+ const installed = await rpc.request("plugin/installed", { cwds: [options.cwd] });
38230
+ return {
38231
+ version: rpc.version,
38232
+ cwd: options.cwd,
38233
+ skills: projectCodexNativeSkillCatalog(response, options.cwd),
38234
+ plugins: projectCodexInstalledPlugins(installed)
38235
+ };
38200
38236
  } finally {
38201
38237
  await rpc.close();
38202
38238
  }
@@ -38241,6 +38277,7 @@ function isCodexNativeSkillDisabled(skill, rules) {
38241
38277
 
38242
38278
  // src/lib/codex-plugin-skill-controls.ts
38243
38279
  var identifier3 = (v) => typeof v === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(v);
38280
+ var CODEX_DEFAULT_AGENT_PLUGIN_VERSION = "1.0.0";
38244
38281
  function refuse3() {
38245
38282
  throw new Error("CODEX_NATIVE_SKILL_IDENTITY_UNSUPPORTED: review native plugin names and hook controls");
38246
38283
  }
@@ -38279,6 +38316,9 @@ function rootControls(root, read) {
38279
38316
  }
38280
38317
  if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier3(manifest.name))
38281
38318
  refuse3();
38319
+ const cacheVersion = basename3(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
38320
+ if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
38321
+ refuse3();
38282
38322
  if (manifest.hooks !== undefined || manifest.mcpServers !== undefined || manifest.commands !== undefined || manifest.apps !== undefined && manifest.apps !== "./.app.json" || ["hooks", ".mcp.json"].some((path) => lstatSync6(join10(root, path), { throwIfNoEntry: false })))
38283
38323
  refuse3();
38284
38324
  const app = join10(root, ".app.json"), present = lstatSync6(app, { throwIfNoEntry: false });
@@ -38355,22 +38395,48 @@ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read) {
38355
38395
  return false;
38356
38396
  }
38357
38397
  }
38358
- function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read) {
38398
+ function reviewCodexPluginSkillControls(catalog, documents, cache, cwd, read, rules) {
38359
38399
  if (catalog?.version !== "codex-cli 0.159.2" || catalog.cwd !== cwd)
38360
38400
  refuse3();
38361
38401
  const skills = projectCodexNativeSkillCatalog({ data: [{ cwd, errors: [], skills: catalog.skills }] }, cwd);
38362
38402
  const allowed = new Set(documents), result = [];
38363
- for (const skill of skills) {
38364
- if (!allowed.has(skill.path) || !skill.path.startsWith(cache + sep2))
38403
+ let installedPlugins;
38404
+ try {
38405
+ installedPlugins = projectCodexInstalledPluginEntries(catalog.plugins ?? []);
38406
+ } catch {
38407
+ refuse3();
38408
+ }
38409
+ const candidates = catalog.plugins === undefined ? skills.map((skill) => skill.path).filter((path) => allowed.has(path)) : allowed;
38410
+ for (const document of candidates) {
38411
+ if (!document.startsWith(cache + sep2))
38365
38412
  continue;
38366
- if (skills.filter((other) => other.name === skill.name).length !== 1)
38413
+ const parsed = identity(document, cache, read);
38414
+ if (parsed.name === "skills-cli" || parsed.name.endsWith(":skills-cli"))
38415
+ refuse3();
38416
+ const cacheParts = relative3(cache, parsed.pluginParent).split(sep2);
38417
+ if (cacheParts.length !== 2 || cacheParts.some((part) => !identifier3(part)))
38367
38418
  refuse3();
38368
- if (!skill.pluginId || skill.name === "skills-cli" || skill.name.endsWith(":skills-cli"))
38419
+ const [marketplace, pluginName] = cacheParts;
38420
+ if (pluginName !== parsed.namespace)
38369
38421
  refuse3();
38370
- const parsed = identity(skill.path, cache, read);
38371
- if (parsed.name !== skill.name)
38422
+ const expectedPluginId = `${pluginName}@${marketplace}`;
38423
+ const listed = skills.filter((skill) => skill.path === document);
38424
+ if (listed.length > 1)
38372
38425
  refuse3();
38373
- result.push({ ...parsed, pluginId: skill.pluginId });
38426
+ if (listed.length === 1) {
38427
+ if (listed[0].name !== parsed.name || listed[0].pluginId !== expectedPluginId)
38428
+ refuse3();
38429
+ } else {
38430
+ const installed = installedPlugins.filter((plugin) => plugin.id === expectedPluginId && plugin.name === pluginName);
38431
+ if (installed.length !== 1 || !installed[0].installed || !installed[0].enabled || !installed[0].localVersion)
38432
+ refuse3();
38433
+ if (installed[0].localVersion !== basename3(dirname5(dirname5(dirname5(document)))))
38434
+ continue;
38435
+ const exactPathDeny = Array.isArray(rules) && rules.some((rule) => rule?.path === document && rule?.enabled === false);
38436
+ if (!exactPathDeny || !isCodexNativeSkillDisabled({ name: parsed.name, path: document, pluginId: expectedPluginId, enabled: true }, rules))
38437
+ refuse3();
38438
+ }
38439
+ result.push({ ...parsed, pluginId: expectedPluginId });
38374
38440
  }
38375
38441
  if (!result.length || new Set(result.map((item) => item.name)).size !== result.length)
38376
38442
  refuse3();
@@ -38532,7 +38598,7 @@ var NATIVE_SKILL_ROOTS = [
38532
38598
  // src/lib/agent-integration.ts
38533
38599
  import { createHash as createHash17, randomUUID as randomUUID3 } from "crypto";
38534
38600
  import { existsSync as existsSync6, lstatSync as lstatSync19, statSync as statSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync14, readdirSync as readdirSync8, opendirSync as opendirSync5, readlinkSync as readlinkSync4, realpathSync as realpathSync7, renameSync as renameSync3, rmdirSync as rmdirSync3, writeFileSync as writeFileSync4, unlinkSync as unlinkSync4, chmodSync, openSync as openSync16, closeSync as closeSync16, fsyncSync as fsyncSync4, fstatSync as fstatSync16, readSync as readSync11, constants as constants12, linkSync as linkSync2 } from "fs";
38535
- import { dirname as dirname15, join as join22, resolve as resolve15, relative as relative3, isAbsolute as isAbsolute17, sep as sep5 } from "path";
38601
+ import { dirname as dirname15, join as join22, resolve as resolve15, relative as relative4, isAbsolute as isAbsolute17, sep as sep5 } from "path";
38536
38602
  import { homedir as homedir3 } from "os";
38537
38603
 
38538
38604
  // src/lib/managed-policy.ts
@@ -38540,7 +38606,7 @@ import { closeSync as closeSync10, constants as constants6, fstatSync as fstatSy
38540
38606
  import { join as join11 } from "path";
38541
38607
 
38542
38608
  // src/lib/agent-policy-limits.ts
38543
- import { basename as basename3, isAbsolute as isAbsolute6, resolve as resolve5 } from "path";
38609
+ import { basename as basename4, isAbsolute as isAbsolute6, resolve as resolve5 } from "path";
38544
38610
  var AGENT_POLICY_LIMITS = Object.freeze({ bytes: 1024 * 1024, agents: 16, discoverySources: 2048, discoveryRawSourceBytes: 64 * 1024 * 1024, discoveryRawTotalBytes: 256 * 1024 * 1024, discoveryPathLinks: 40, discoveryPathSteps: 256, discoveryPathSourceMetadataBytes: 64 * 1024, discoveryPathTotalMetadataBytes: 8 * 1024 * 1024, discoveryRoots: 512, discoveryDirectories: 64, discoveryDirectoryEntries: 20000, discoveryDirectoryBytes: 8 * 1024 * 1024, builtinNames: 2048, rootAliases: 2, fields: 64, pathCharacters: 4096 });
38545
38611
  function object3(value) {
38546
38612
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -38619,11 +38685,11 @@ function assertAgentPolicyCollections(policy) {
38619
38685
  if (source.hashMode !== undefined)
38620
38686
  requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1", "claude-settings-v2", "claude-settings-v3", "claude-marketplace-registry-v2", "codex-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
38621
38687
  if (source.hashMode === "codex-settings-v1")
38622
- requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute6(source.path) && resolve5(source.path) === source.path && basename3(source.path) === "config.toml");
38688
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute6(source.path) && resolve5(source.path) === source.path && basename4(source.path) === "config.toml");
38623
38689
  if (source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3")
38624
- requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute6(source.path) && resolve5(source.path) === source.path && basename3(source.path) === "settings.json");
38690
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute6(source.path) && resolve5(source.path) === source.path && basename4(source.path) === "settings.json");
38625
38691
  if (source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2")
38626
- requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute6(source.path) && resolve5(source.path) === source.path && basename3(source.path) === "known_marketplaces.json");
38692
+ requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute6(source.path) && resolve5(source.path) === source.path && basename4(source.path) === "known_marketplaces.json");
38627
38693
  if (source.hashMode === "claude-plugin-registry") {
38628
38694
  requireBound(agent === "claude");
38629
38695
  const managed = array(source.managedPlugins, 64);
@@ -38777,7 +38843,7 @@ function isOwnedCliBridge(path, expectedPaths) {
38777
38843
  // src/lib/agent-discovery.ts
38778
38844
  import { createHash as createHash16 } from "crypto";
38779
38845
  import { lstatSync as lstatSync18, readFileSync as readFileSync13, readdirSync as readdirSync7, realpathSync as realpathSync6 } from "fs";
38780
- import { basename as basename6, dirname as dirname14, isAbsolute as isAbsolute16, join as join21, resolve as resolve14, sep as sep4 } from "path";
38846
+ import { basename as basename7, dirname as dirname14, isAbsolute as isAbsolute16, join as join21, resolve as resolve14, sep as sep4 } from "path";
38781
38847
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
38782
38848
 
38783
38849
  // node_modules/yaml/dist/index.js
@@ -39059,7 +39125,7 @@ function capture(path, budget) {
39059
39125
  return { path, sha256: null };
39060
39126
  need3(root.isDirectory(), "Unsupported native discovery directory input");
39061
39127
  const hash2 = createHash8("sha256");
39062
- function walk2(current, relative3, before, depth) {
39128
+ function walk2(current, relative4, before, depth) {
39063
39129
  need3(depth <= 64, "Native discovery directory depth limit exceeded");
39064
39130
  const names = [], directory = opendirSync2(current);
39065
39131
  try {
@@ -39072,7 +39138,7 @@ function capture(path, budget) {
39072
39138
  }
39073
39139
  need3(new Set(names).size === names.length, "Ambiguous native discovery directory names");
39074
39140
  for (const name of names.sort()) {
39075
- const child = join14(current, name), childRelative = relative3 ? `${relative3}/${name}` : name;
39141
+ const child = join14(current, name), childRelative = relative4 ? `${relative4}/${name}` : name;
39076
39142
  need3(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
39077
39143
  const stat = lstatSync10(child);
39078
39144
  need3(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
@@ -39288,7 +39354,7 @@ function hashDiscoveryPathFile(path, budget, changes) {
39288
39354
 
39289
39355
  // src/lib/plugin-discovery.ts
39290
39356
  import { closeSync as closeSync13, constants as constants9, fstatSync as fstatSync13, lstatSync as lstatSync15, openSync as openSync13, opendirSync as opendirSync4, readSync as readSync8 } from "fs";
39291
- import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute14, join as join20, resolve as resolve12 } from "path";
39357
+ import { basename as basename5, dirname as dirname11, isAbsolute as isAbsolute14, join as join20, resolve as resolve12 } from "path";
39292
39358
 
39293
39359
  // src/lib/plugin-admission.ts
39294
39360
  import { homedir as homedir2 } from "os";
@@ -39631,7 +39697,7 @@ function directoryNames(path) {
39631
39697
  function snapshotPluginTree(root, options = {}) {
39632
39698
  const chain = ancestors(root), entries = [], files = [], directories = [];
39633
39699
  let bytes = 0, visited = 0;
39634
- const visit2 = (directory, relative3) => {
39700
+ const visit2 = (directory, relative4) => {
39635
39701
  const before = stat(directory);
39636
39702
  pluginNeed(before && before.isDirectory() && !before.isSymbolicLink(), "Plugin tree must contain real directories");
39637
39703
  const names = directoryNames(directory);
@@ -39639,18 +39705,18 @@ function snapshotPluginTree(root, options = {}) {
39639
39705
  directories.push({ path: directory, stat: before, names });
39640
39706
  for (const name of names) {
39641
39707
  pluginNeed(++visited <= PLUGIN_PROJECTION_LIMITS.files * 50, "Plugin tree membership exceeds its limit");
39642
- const path = relative3 ? `${relative3}/${name}` : name;
39708
+ const path = relative4 ? `${relative4}/${name}` : name;
39643
39709
  pluginNeed(new TextEncoder().encode(path).byteLength <= 100 && !/[\\:\x00-\x1f\x7f]/u.test(path), "Plugin entry path exceeds its limit or is unsafe");
39644
39710
  const absolute = join16(directory, name), entryStat = stat(absolute);
39645
39711
  pluginNeed(entryStat, "Plugin tree member disappeared");
39646
- if (relative3 === "" && name === ".orphaned_at" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
39712
+ if (relative4 === "" && name === ".orphaned_at" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
39647
39713
  const bytes2 = readBytes(absolute, 32, "native-pin");
39648
39714
  pluginNeed(/^[1-9][0-9]{12}$/.test(new TextDecoder().decode(bytes2)), "Malformed native orphan timestamp");
39649
39715
  same3(entryStat, stat(absolute));
39650
39716
  files.push({ path: absolute, stat: entryStat });
39651
39717
  continue;
39652
39718
  }
39653
- if (relative3 === "" && name === ".in_use" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
39719
+ if (relative4 === "" && name === ".in_use" && (options.nativeRuntime === "claude-2.1.274" || options.nativeRuntime === "claude-2.1.276")) {
39654
39720
  pluginNeed(entryStat.isDirectory() && !entryStat.isSymbolicLink(), "Native plugin pins require a real directory");
39655
39721
  owner(entryStat);
39656
39722
  const pins = directoryNames(absolute);
@@ -40769,7 +40835,7 @@ function hashManagedPluginRegistry(path, managed) {
40769
40835
  pluginNeed(/^(?:[A-Za-z0-9][A-Za-z0-9._+-]*-)?[a-f0-9]{12}$/.test(row.version) && row.installPath === join20(cacheRoot, row.version), "Managed plugin cache path or version is outside its registration");
40770
40836
  const approval = (producer) => {
40771
40837
  pluginText(producer);
40772
- const digest2 = `sha256:${basename4(producer)}`;
40838
+ const digest2 = `sha256:${basename5(producer)}`;
40773
40839
  const receipt2 = readPluginAdmissionReceipt(witness.storeRoot, witness.bindingId, digest2);
40774
40840
  pluginNeed(receipt2.materializedPath === producer, "Managed producer path is not an approved immutable projection");
40775
40841
  if (!verified.has(producer)) {
@@ -40833,7 +40899,7 @@ function captureManagedPluginRegistry(path, managedPlugins) {
40833
40899
  // src/lib/claude-marketplace-registry.ts
40834
40900
  import { createHash as createHash13 } from "crypto";
40835
40901
  import { closeSync as closeSync14, constants as constants10, fstatSync as fstatSync14, lstatSync as lstatSync16, openSync as openSync14, readSync as readSync9 } from "fs";
40836
- import { basename as basename5, dirname as dirname12, isAbsolute as isAbsolute15, resolve as resolve13 } from "path";
40902
+ import { basename as basename6, dirname as dirname12, isAbsolute as isAbsolute15, resolve as resolve13 } from "path";
40837
40903
  var CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
40838
40904
  function need4(value, reason) {
40839
40905
  if (!value)
@@ -40855,7 +40921,7 @@ function parents2(path) {
40855
40921
  }
40856
40922
  function readRegistry2(path, budget) {
40857
40923
  absolutePath3(path);
40858
- need4(basename5(path) === "known_marketplaces.json", "requires known_marketplaces.json");
40924
+ need4(basename6(path) === "known_marketplaces.json", "requires known_marketplaces.json");
40859
40925
  need4(Number.isSafeInteger(budget.remaining) && budget.remaining >= 0, "has an invalid byte budget");
40860
40926
  const ancestors2 = parents2(path), initial = lstatSync16(path, { bigint: true });
40861
40927
  need4(initial.isFile() && !initial.isSymbolicLink() && initial.size <= BigInt(CLAUDE_MARKETPLACE_REGISTRY_LIMITS.bytes), "is not a bounded regular file");
@@ -41195,9 +41261,9 @@ function verifyAgentDiscovery(binding, codexRecovery) {
41195
41261
  safe4(root2);
41196
41262
  }
41197
41263
  function assertMarketplaceBinding(binding, source) {
41198
- if (source.hashMode === "codex-settings-v1" && (binding.agent !== "codex" || binding.method !== "reviewed" || basename6(source.path) !== "config.toml"))
41264
+ if (source.hashMode === "codex-settings-v1" && (binding.agent !== "codex" || binding.method !== "reviewed" || basename7(source.path) !== "config.toml"))
41199
41265
  throw new Error("Codex settings witnesses require explicit reviewed Codex configuration");
41200
- if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename6(source.path) !== "settings.json"))
41266
+ if ((source.hashMode === "claude-settings-v1" || source.hashMode === "claude-settings-v2" || source.hashMode === "claude-settings-v3") && (binding.agent !== "claude" || binding.method !== "reviewed" || basename7(source.path) !== "settings.json"))
41201
41267
  throw new Error("Claude settings witnesses require explicit reviewed Claude configuration");
41202
41268
  if ((source.hashMode === "claude-marketplace-registry" || source.hashMode === "claude-marketplace-registry-v2") && (binding.agent !== "claude" || binding.method !== "reviewed"))
41203
41269
  throw new Error("Claude marketplace witnesses require explicit reviewed Claude discovery");
@@ -41833,7 +41899,7 @@ function readRootAlias(home, agent) {
41833
41899
  const targetStat = lstatSync19(targetPath, { throwIfNoEntry: false });
41834
41900
  if (!targetStat?.isDirectory())
41835
41901
  throw new Error(`Agent root alias must target an existing directory: ${alias}`);
41836
- const target = realpathSync7(targetPath), inside = relative3(realHome, target);
41902
+ const target = realpathSync7(targetPath), inside = relative4(realHome, target);
41837
41903
  if (!inside || inside === ".." || inside.startsWith(`..${sep5}`) || isAbsolute17(inside))
41838
41904
  throw new Error(`Agent root alias target must be inside the real home: ${alias}`);
41839
41905
  return { agent, home: realHome, alias, target, link, aliasIdentity: `${stat2.dev}:${stat2.ino}:${stat2.ctimeNs}`, targetIdentity: `${targetStat.dev}:${targetStat.ino}` };
@@ -41851,7 +41917,7 @@ function rootAliases(home, allowed = false) {
41851
41917
  }
41852
41918
  function canonicalAgentPath(path, aliases) {
41853
41919
  const absolute = resolve15(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep5));
41854
- return binding ? join22(binding.target, relative3(binding.alias, absolute)) : absolute;
41920
+ return binding ? join22(binding.target, relative4(binding.alias, absolute)) : absolute;
41855
41921
  }
41856
41922
  function disabledCodexSkillPaths(config, aliases) {
41857
41923
  const skills = config.skills;
@@ -41922,7 +41988,7 @@ function treeHash(root2) {
41922
41988
  const stat2 = lstatSync19(path);
41923
41989
  if (stat2.isSymbolicLink())
41924
41990
  throw new Error(`Refusing symlink in skill: ${path}`);
41925
- const rel = relative3(root2, path);
41991
+ const rel = relative4(root2, path);
41926
41992
  if (stat2.isDirectory()) {
41927
41993
  hash2.update(`d\x00${rel}\x00`);
41928
41994
  for (const name of readdirSync8(path).sort())
@@ -41948,7 +42014,7 @@ function selectNativeMigrationTargets(inventory, manifest) {
41948
42014
  const identities = new Set;
41949
42015
  const selected = [];
41950
42016
  for (const target of manifest.targets) {
41951
- const projectRoot = resolve15(target.projectRoot), normalized = relative3(projectRoot, resolve15(projectRoot, target.path));
42017
+ const projectRoot = resolve15(target.projectRoot), normalized = relative4(projectRoot, resolve15(projectRoot, target.path));
41952
42018
  if (!isAbsolute17(target.projectRoot) || projectRoot !== target.projectRoot || !existsSync6(projectRoot) || !lstatSync19(projectRoot).isDirectory())
41953
42019
  throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
41954
42020
  assertSafePath(projectRoot);
@@ -42303,7 +42369,7 @@ function planAgentIntegration(options) {
42303
42369
  const bytes = readNativeBytes(path, 1024 * 1024);
42304
42370
  observedNativeSources.push({ path, sha256: sha3(bytes) });
42305
42371
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
42306
- }) : policy.bridge?.codexPluginSkills ?? [];
42372
+ }, codexConfig.skills?.config ?? []) : policy.bridge?.codexPluginSkills ?? [];
42307
42373
  const changes = [];
42308
42374
  for (const agent of [...new Set(options.agents)]) {
42309
42375
  if (!INTEGRATION_AGENTS.includes(agent))
@@ -42381,7 +42447,7 @@ function planAgentIntegration(options) {
42381
42447
  profiles: Object.fromEntries([...new Set([...priorAgents, ...options.agents])].sort().map((agent) => [agent, bindings.get(agent)?.profileId ?? policy.bridge?.profiles?.[agent] ?? policy.profileId])),
42382
42448
  disabledBuiltins: options.agents.includes("codex") ? nativeSkills.filter((entry) => entry.agent === "codex" && entry.vendor && entry.path.startsWith(canonicalAgentPath(join22(home, ".codex", "skills", ".system"), aliases) + sep5)).map((entry) => ({ path: entry.path, hash: entry.hash })) : policy.bridge?.disabledBuiltins ?? [],
42383
42449
  rootAliases: aliases,
42384
- ...codexPluginSkills.length ? { codexPluginSkills, codexPluginSkillReview: options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd) })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join22(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join22(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview } : {},
42450
+ ...codexPluginSkills.length ? { codexPluginSkills, codexPluginSkillReview: options.codexNativeCatalog ? { version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, catalogSha256: sha3(JSON.stringify({ version: options.codexNativeCatalog.version, cwd: options.codexNativeCatalog.cwd, skills: projectCodexNativeSkillCatalog({ data: [{ cwd: options.codexNativeCatalog.cwd, errors: [], skills: options.codexNativeCatalog.skills }] }, options.codexNativeCatalog.cwd), ...options.codexNativeCatalog.plugins === undefined ? {} : { plugins: projectCodexInstalledPluginEntries(options.codexNativeCatalog.plugins) } })), configSha256: changes.find((change) => change.path === canonicalAgentPath(join22(home, ".codex/config.toml"), aliases))?.before === null ? null : sha3(readOptional(canonicalAgentPath(join22(home, ".codex/config.toml"), aliases)) ?? "") } : policy.bridge?.codexPluginSkillReview } : {},
42385
42451
  discovery: { ...policy.bridge?.discovery, ...Object.fromEntries(discoveryAfter.map((binding) => [binding.agent, binding])) }
42386
42452
  } };
42387
42453
  const serializedPolicy = serializeManagedSkillPolicy(nextPolicy);
@@ -42399,7 +42465,7 @@ function planClaudePreToolUseHookUpdate(options) {
42399
42465
  function claudeSettingsConsumers(home, path, discoveries, aliases) {
42400
42466
  const canonical5 = (value) => canonicalAgentPath(value, aliases);
42401
42467
  const containsSettings = (root2) => {
42402
- const distance = relative3(canonical5(root2), path);
42468
+ const distance = relative4(canonical5(root2), path);
42403
42469
  return distance === "" || distance !== ".." && !distance.startsWith(`..${sep5}`) && !isAbsolute17(distance);
42404
42470
  };
42405
42471
  return discoveries.filter((binding) => binding.agent === "claude" || binding.agent === "opencode" || canonical5(join22(home, AGENT_ADAPTERS[binding.agent].config)) === path || binding.sources.some((source) => canonical5(source.path) === path) || binding.roots.some(containsSettings) || binding.directories?.some((directory) => containsSettings(directory.path))).map((binding) => binding.agent);
@@ -42913,7 +42979,7 @@ import {
42913
42979
  statSync as statSync9,
42914
42980
  writeFileSync as writeFileSync6
42915
42981
  } from "fs";
42916
- import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute18, join as join26, normalize as normalize3, relative as relative5, resolve as resolve17, sep as sep8 } from "path";
42982
+ import { basename as basename10, dirname as dirname18, isAbsolute as isAbsolute18, join as join26, normalize as normalize3, relative as relative6, resolve as resolve17, sep as sep8 } from "path";
42917
42983
 
42918
42984
  // src/lib/registry-data/index.ts
42919
42985
  var SKILLS = [];
@@ -43438,7 +43504,7 @@ import {
43438
43504
  realpathSync as realpathSync8,
43439
43505
  writeFileSync as writeFileSync5
43440
43506
  } from "fs";
43441
- import { basename as basename7, dirname as dirname16, join as join24, relative as relative4 } from "path";
43507
+ import { basename as basename8, dirname as dirname16, join as join24, relative as relative5 } from "path";
43442
43508
  var ANY_SEGMENT_COPY_EXCLUDES = new Set([
43443
43509
  ".git",
43444
43510
  ".DS_Store",
@@ -43488,11 +43554,11 @@ function normalizeNewPortableSkillName(name) {
43488
43554
  throw new Error(`Invalid skill name '${name}'. Include letters or numbers.`);
43489
43555
  return normalized;
43490
43556
  }
43491
- function readPortableSkillManifest(skillPath, fallbackName = basename7(skillPath)) {
43557
+ function readPortableSkillManifest(skillPath, fallbackName = basename8(skillPath)) {
43492
43558
  return readManifest(skillPath, fallbackName, normalizePortableSkillName);
43493
43559
  }
43494
43560
  function readPortableSkillManifestForImport(skillPath) {
43495
- return readManifest(skillPath, basename7(skillPath), normalizeNewPortableSkillName);
43561
+ return readManifest(skillPath, basename8(skillPath), normalizeNewPortableSkillName);
43496
43562
  }
43497
43563
  function readManifest(skillPath, fallbackName, normalizeName) {
43498
43564
  const skillJsonPath = join24(skillPath, "skill.json");
@@ -43795,7 +43861,7 @@ function copySkillDirectory(source, destination) {
43795
43861
  cpSync(resolvedSource, destination, {
43796
43862
  recursive: true,
43797
43863
  filter: (src) => {
43798
- const rel = relative4(resolvedSource, src);
43864
+ const rel = relative5(resolvedSource, src);
43799
43865
  if (!rel)
43800
43866
  return true;
43801
43867
  const segments = rel.split(/[\\/]/);
@@ -44046,7 +44112,7 @@ function inferRuntimeFromCommands(commands) {
44046
44112
  function readJsonObject(path) {
44047
44113
  const parsed = JSON.parse(readFileSync16(path, "utf-8"));
44048
44114
  if (!isRecord2(parsed))
44049
- throw new Error(`${basename7(path)} must contain a JSON object`);
44115
+ throw new Error(`${basename8(path)} must contain a JSON object`);
44050
44116
  return parsed;
44051
44117
  }
44052
44118
  function hasPackageDependencies(pkgPath) {
@@ -44084,7 +44150,7 @@ function escapeJsString(value) {
44084
44150
  // src/lib/authoring-path.ts
44085
44151
  import { lstatSync as lstatSync21, realpathSync as realpathSync9 } from "fs";
44086
44152
  import { homedir as homedir4 } from "os";
44087
- import { basename as basename8, dirname as dirname17, join as join25, resolve as resolve16, sep as sep7 } from "path";
44153
+ import { basename as basename9, dirname as dirname17, join as join25, resolve as resolve16, sep as sep7 } from "path";
44088
44154
  var NATIVE_DISCOVERY_ROOTS = NATIVE_SKILL_ROOTS.map(([, path]) => path.split("/"));
44089
44155
  function pathParts(path) {
44090
44156
  return resolve16(path).split(sep7).filter(Boolean);
@@ -44104,7 +44170,7 @@ function physicalPath(path) {
44104
44170
  let cursor = resolve16(path);
44105
44171
  const suffix = [];
44106
44172
  while (!lstatSync21(cursor, { throwIfNoEntry: false })) {
44107
- suffix.unshift(basename8(cursor));
44173
+ suffix.unshift(basename9(cursor));
44108
44174
  const parent = dirname17(cursor);
44109
44175
  if (parent === cursor)
44110
44176
  return resolve16(path);
@@ -44323,7 +44389,7 @@ function physicalImportPath(path) {
44323
44389
  let ancestor = resolve17(path);
44324
44390
  const suffix = [];
44325
44391
  while (!existsSync9(ancestor)) {
44326
- suffix.unshift(basename9(ancestor));
44392
+ suffix.unshift(basename10(ancestor));
44327
44393
  ancestor = dirname18(ancestor);
44328
44394
  }
44329
44395
  return join26(realpathSync10(ancestor), ...suffix);
@@ -44332,7 +44398,7 @@ function assertDisjointImportPaths(source, destination) {
44332
44398
  const sourcePath = realpathSync10(source);
44333
44399
  const destinationPath = physicalImportPath(destination);
44334
44400
  const contains2 = (parent, child) => {
44335
- const path = relative5(parent, child);
44401
+ const path = relative6(parent, child);
44336
44402
  return path === "" || !isAbsolute18(path) && path !== ".." && !path.startsWith(`..${sep8}`);
44337
44403
  };
44338
44404
  if (contains2(sourcePath, destinationPath) || contains2(destinationPath, sourcePath)) {
@@ -44348,8 +44414,8 @@ function portPortableSkill(sourcePath, options = {}) {
44348
44414
  const explicitName = options.name != null;
44349
44415
  const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
44350
44416
  if (isOfficialSkillName(skillName) && !options.allowShadow) {
44351
- const sourceSlug = safeNormalizeName(basename9(absoluteSource));
44352
- const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename9(absoluteSource)}')` : ""} matches a bundled official skill.`;
44417
+ const sourceSlug = safeNormalizeName(basename10(absoluteSource));
44418
+ const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename10(absoluteSource)}')` : ""} matches a bundled official skill.`;
44353
44419
  throw new Error(`${via} Importing it would shadow the official '${skillName}'. ` + `Pass --name to choose a different name, or --allow-shadow to override deliberately.`);
44354
44420
  }
44355
44421
  const readOnlyDestination = join26(resolvePortableSkillsRoot(options, false), skillName);
@@ -44869,7 +44935,7 @@ function writeManagedSkillDir(dir, skillMd, options) {
44869
44935
  return { action, path: skillMdPath };
44870
44936
  const parentDir = dirname19(dir);
44871
44937
  mkdirSync8(parentDir, { recursive: true });
44872
- const transactionDir = mkdtempSync2(join27(parentDir, `.hasna-skills-write-${basename10(dir)}-`));
44938
+ const transactionDir = mkdtempSync2(join27(parentDir, `.hasna-skills-write-${basename11(dir)}-`));
44873
44939
  const candidateDir = join27(transactionDir, "candidate");
44874
44940
  const backupDir = join27(transactionDir, "backup");
44875
44941
  const candidateSkillMdPath = join27(candidateDir, "SKILL.md");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/skills",
3
- "version": "0.10.18",
3
+ "version": "0.10.20",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {