@hasna/skills 0.10.66 → 0.10.68

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/README.md CHANGED
@@ -2534,6 +2534,28 @@ untrusted byte entries, not arbitrary JavaScript proxies or hostile host code.
2534
2534
  `ContentHashInputError` reports fixed invalid-input, limit, abort or timeout codes
2535
2535
  without including file contents or paths in its messages.
2536
2536
 
2537
+ ### Requirements from explicit content
2538
+
2539
+ `@hasna/skills/sdk` exports `getSkillRequirementsFromContent`, the parser behind
2540
+ `skills requires`, for content the caller already holds, such as verified
2541
+ bundle entries:
2542
+
2543
+ ```ts
2544
+ import { getSkillRequirementsFromContent, type SkillMeta, type SkillRequirements } from "@hasna/skills/sdk";
2545
+
2546
+ declare const meta: SkillMeta | undefined; // the skill's catalog record, if known
2547
+ const requirements: SkillRequirements = getSkillRequirementsFromContent(
2548
+ "write-brief", [skillMd, readme, envExample], packageJson.dependencies, meta);
2549
+ // { envVars, systemDeps, cliCommand: "skills run write-brief", dependencies }
2550
+ ```
2551
+
2552
+ The result depends only on the arguments. It reads no registry, file,
2553
+ environment, install state or network, and does not change its inputs; the
2554
+ returned `dependencies` is the object you passed. When `meta.tags` includes
2555
+ `premium` or `remote`, the skill is treated as hosted: provider keys such as
2556
+ `OPENAI_API_KEY` are omitted, `SKILLS_API_KEY` and `SKILL_API_KEY` are dropped,
2557
+ and `HASNA_SKILLS_API_KEY` is listed.
2558
+
2537
2559
  ## Invitation email recovery
2538
2560
 
2539
2561
  An existing account with no usable membership can explicitly recover access by
package/bin/index.js CHANGED
@@ -36888,7 +36888,7 @@ var package_default;
36888
36888
  var init_package = __esm(() => {
36889
36889
  package_default = {
36890
36890
  name: "@hasna/skills",
36891
- version: "0.10.66",
36891
+ version: "0.10.68",
36892
36892
  description: "Skills library for AI coding agents",
36893
36893
  type: "module",
36894
36894
  bin: {
@@ -40299,7 +40299,8 @@ var init_codex_native_compatibility = __esm(() => {
40299
40299
  "codex-cli 0.160.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
40300
40300
  "codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
40301
40301
  "codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
40302
- "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
40302
+ "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
40303
+ "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
40303
40304
  };
40304
40305
  SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version) => supportsCodexNativeCapability(version, "hooks")));
40305
40306
  });
@@ -53828,7 +53829,9 @@ function codexLocalMarketplaceMapsPlugin(text2, marketplace, plugin) {
53828
53829
  try {
53829
53830
  hashNativeJsonControls(text2);
53830
53831
  const value = JSON.parse(text2);
53831
- if (!plainObject(value) || !exactKeys(value, ["interface", "name", "plugins"]) || value.name !== marketplace || !(typeof value.interface === "string" || Number.isSafeInteger(value.interface)) || !Array.isArray(value.plugins) || value.plugins.length < 1 || value.plugins.length > 4096)
53832
+ const marketplaceInterface = plainObject(value) ? value.interface : undefined;
53833
+ const validInterface = typeof marketplaceInterface === "string" || Number.isSafeInteger(marketplaceInterface) || plainObject(marketplaceInterface) && exactKeys(marketplaceInterface, [], ["displayName"]) && typeof marketplaceInterface.displayName === "string" && marketplaceInterface.displayName.trim().length > 0;
53834
+ if (!plainObject(value) || !exactKeys(value, ["interface", "name", "plugins"]) || value.name !== marketplace || !validInterface || !Array.isArray(value.plugins) || value.plugins.length < 1 || value.plugins.length > 4096)
53832
53835
  return false;
53833
53836
  const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
53834
53837
  if (rows.length !== 1)
@@ -54185,7 +54188,7 @@ function reviewedHookControls(root, read2) {
54185
54188
  if (!plainObject(manifest) || !exactKeys(manifest, ["name", "version", "hooks"], ["description"]) || !identifier3(manifest.name) || typeof manifest.version !== "string" || manifest.version !== basename9(root) || manifest.hooks !== "./hooks/hooks.json" || manifest.description !== undefined && (typeof manifest.description !== "string" || !manifest.description.trim() || manifest.description.length > 1024))
54186
54189
  refuse4();
54187
54190
  const manifestRecord = manifest;
54188
- if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"]) || !plainObject(hooks.hooks))
54191
+ if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"], ["description"]) || !plainObject(hooks.hooks) || hooks.description !== undefined && typeof hooks.description !== "string")
54189
54192
  refuse4();
54190
54193
  const hookTable = hooks.hooks;
54191
54194
  const events = Object.entries(hookTable);
@@ -54312,7 +54315,7 @@ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files,
54312
54315
  refuse4();
54313
54316
  if ([".app.json", ".mcp.json"].includes(base2) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
54314
54317
  refuse4();
54315
- if (parts.includes("hooks") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base2 !== "hooks" : base2 !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join25(cacheRoot, parts[0], parts[1]))))
54318
+ if ((parts[3] === "hooks" || base2 === "hooks.json") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base2 !== "hooks" : base2 !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join25(cacheRoot, parts[0], parts[1]))))
54316
54319
  refuse4();
54317
54320
  }
54318
54321
  }
@@ -59211,9 +59214,7 @@ function generateSkillMd(name) {
59211
59214
  function extractEnvVars(text2) {
59212
59215
  const envVars = new Set;
59213
59216
  for (const pattern of [ENV_VAR_PATTERN, GENERIC_ENV_PATTERN]) {
59214
- pattern.lastIndex = 0;
59215
- let match;
59216
- while ((match = pattern.exec(text2)) !== null) {
59217
+ for (const match of text2.matchAll(pattern)) {
59217
59218
  envVars.add(match[1]);
59218
59219
  }
59219
59220
  }
package/bin/mcp.js CHANGED
@@ -23,7 +23,7 @@ var package_default;
23
23
  var init_package = __esm(() => {
24
24
  package_default = {
25
25
  name: "@hasna/skills",
26
- version: "0.10.66",
26
+ version: "0.10.68",
27
27
  description: "Skills library for AI coding agents",
28
28
  type: "module",
29
29
  bin: {
@@ -22755,7 +22755,8 @@ var init_codex_native_compatibility = __esm(() => {
22755
22755
  "codex-cli 0.160.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
22756
22756
  "codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
22757
22757
  "codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
22758
- "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
22758
+ "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
22759
+ "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
22759
22760
  };
22760
22761
  SUPPORTED_CODEX_HOOK_VERSIONS = Object.freeze(Object.keys(MEASURED_CODEX_CAPABILITIES).filter((version2) => supportsCodexNativeCapability(version2, "hooks")));
22761
22762
  });
@@ -33706,7 +33707,9 @@ function codexLocalMarketplaceMapsPlugin(text2, marketplace, plugin) {
33706
33707
  try {
33707
33708
  hashNativeJsonControls(text2);
33708
33709
  const value = JSON.parse(text2);
33709
- if (!plainObject(value) || !exactKeys(value, ["interface", "name", "plugins"]) || value.name !== marketplace || !(typeof value.interface === "string" || Number.isSafeInteger(value.interface)) || !Array.isArray(value.plugins) || value.plugins.length < 1 || value.plugins.length > 4096)
33710
+ const marketplaceInterface = plainObject(value) ? value.interface : undefined;
33711
+ const validInterface = typeof marketplaceInterface === "string" || Number.isSafeInteger(marketplaceInterface) || plainObject(marketplaceInterface) && exactKeys(marketplaceInterface, [], ["displayName"]) && typeof marketplaceInterface.displayName === "string" && marketplaceInterface.displayName.trim().length > 0;
33712
+ if (!plainObject(value) || !exactKeys(value, ["interface", "name", "plugins"]) || value.name !== marketplace || !validInterface || !Array.isArray(value.plugins) || value.plugins.length < 1 || value.plugins.length > 4096)
33710
33713
  return false;
33711
33714
  const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
33712
33715
  if (rows.length !== 1)
@@ -34063,7 +34066,7 @@ function reviewedHookControls(root, read) {
34063
34066
  if (!plainObject(manifest) || !exactKeys(manifest, ["name", "version", "hooks"], ["description"]) || !identifier3(manifest.name) || typeof manifest.version !== "string" || manifest.version !== basename7(root) || manifest.hooks !== "./hooks/hooks.json" || manifest.description !== undefined && (typeof manifest.description !== "string" || !manifest.description.trim() || manifest.description.length > 1024))
34064
34067
  refuse3();
34065
34068
  const manifestRecord = manifest;
34066
- if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"]) || !plainObject(hooks.hooks))
34069
+ if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"], ["description"]) || !plainObject(hooks.hooks) || hooks.description !== undefined && typeof hooks.description !== "string")
34067
34070
  refuse3();
34068
34071
  const hookTable = hooks.hooks;
34069
34072
  const events = Object.entries(hookTable);
@@ -34190,7 +34193,7 @@ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files,
34190
34193
  refuse3();
34191
34194
  if ([".app.json", ".mcp.json"].includes(base) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
34192
34195
  refuse3();
34193
- if (parts.includes("hooks") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base !== "hooks" : base !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join19(cacheRoot, parts[0], parts[1]))))
34196
+ if ((parts[3] === "hooks" || base === "hooks.json") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base !== "hooks" : base !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join19(cacheRoot, parts[0], parts[1]))))
34194
34197
  refuse3();
34195
34198
  }
34196
34199
  }
@@ -35475,9 +35478,7 @@ function detectProjectSkills(cwd = process.cwd()) {
35475
35478
  function extractEnvVars(text2) {
35476
35479
  const envVars = new Set;
35477
35480
  for (const pattern of [ENV_VAR_PATTERN, GENERIC_ENV_PATTERN]) {
35478
- pattern.lastIndex = 0;
35479
- let match;
35480
- while ((match = pattern.exec(text2)) !== null) {
35481
+ for (const match of text2.matchAll(pattern)) {
35481
35482
  envVars.add(match[1]);
35482
35483
  }
35483
35484
  }
package/bin/migrate.js CHANGED
@@ -25,7 +25,7 @@ import { join as join5 } from "path";
25
25
  // package.json
26
26
  var package_default = {
27
27
  name: "@hasna/skills",
28
- version: "0.10.66",
28
+ version: "0.10.68",
29
29
  description: "Skills library for AI coding agents",
30
30
  type: "module",
31
31
  bin: {
package/bin/server.js CHANGED
@@ -24140,7 +24140,7 @@ restoreLaunchCwd();
24140
24140
  // package.json
24141
24141
  var package_default = {
24142
24142
  name: "@hasna/skills",
24143
- version: "0.10.66",
24143
+ version: "0.10.68",
24144
24144
  description: "Skills library for AI coding agents",
24145
24145
  type: "module",
24146
24146
  bin: {
package/bin/worker.js CHANGED
@@ -24143,7 +24143,7 @@ import { randomUUID as randomUUID6 } from "crypto";
24143
24143
  // package.json
24144
24144
  var package_default = {
24145
24145
  name: "@hasna/skills",
24146
- version: "0.10.66",
24146
+ version: "0.10.68",
24147
24147
  description: "Skills library for AI coding agents",
24148
24148
  type: "module",
24149
24149
  bin: {
package/dist/index.js CHANGED
@@ -9400,7 +9400,8 @@ var MEASURED_CODEX_CAPABILITIES = {
9400
9400
  "codex-cli 0.160.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
9401
9401
  "codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
9402
9402
  "codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
9403
- "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
9403
+ "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
9404
+ "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
9404
9405
  };
9405
9406
  function supportsCodexNativeCapability(version, capability) {
9406
9407
  return typeof version === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version) && MEASURED_CODEX_CAPABILITIES[version].includes(capability);
@@ -11922,7 +11923,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11922
11923
  });
11923
11924
  }
11924
11925
  // package.json
11925
- var version = "0.10.66";
11926
+ var version = "0.10.68";
11926
11927
  var package_default = {
11927
11928
  name: "@hasna/skills",
11928
11929
  version,
@@ -16225,7 +16226,9 @@ function codexLocalMarketplaceMapsPlugin(text2, marketplace, plugin) {
16225
16226
  try {
16226
16227
  hashNativeJsonControls(text2);
16227
16228
  const value = JSON.parse(text2);
16228
- if (!plainObject(value) || !exactKeys(value, ["interface", "name", "plugins"]) || value.name !== marketplace || !(typeof value.interface === "string" || Number.isSafeInteger(value.interface)) || !Array.isArray(value.plugins) || value.plugins.length < 1 || value.plugins.length > 4096)
16229
+ const marketplaceInterface = plainObject(value) ? value.interface : undefined;
16230
+ const validInterface = typeof marketplaceInterface === "string" || Number.isSafeInteger(marketplaceInterface) || plainObject(marketplaceInterface) && exactKeys(marketplaceInterface, [], ["displayName"]) && typeof marketplaceInterface.displayName === "string" && marketplaceInterface.displayName.trim().length > 0;
16231
+ if (!plainObject(value) || !exactKeys(value, ["interface", "name", "plugins"]) || value.name !== marketplace || !validInterface || !Array.isArray(value.plugins) || value.plugins.length < 1 || value.plugins.length > 4096)
16229
16232
  return false;
16230
16233
  const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
16231
16234
  if (rows.length !== 1)
@@ -16601,7 +16604,7 @@ function reviewedHookControls(root, read2) {
16601
16604
  if (!plainObject(manifest) || !exactKeys(manifest, ["name", "version", "hooks"], ["description"]) || !identifier3(manifest.name) || typeof manifest.version !== "string" || manifest.version !== basename8(root) || manifest.hooks !== "./hooks/hooks.json" || manifest.description !== undefined && (typeof manifest.description !== "string" || !manifest.description.trim() || manifest.description.length > 1024))
16602
16605
  refuse4();
16603
16606
  const manifestRecord = manifest;
16604
- if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"]) || !plainObject(hooks.hooks))
16607
+ if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"], ["description"]) || !plainObject(hooks.hooks) || hooks.description !== undefined && typeof hooks.description !== "string")
16605
16608
  refuse4();
16606
16609
  const hookTable = hooks.hooks;
16607
16610
  const events = Object.entries(hookTable);
@@ -16728,7 +16731,7 @@ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files,
16728
16731
  refuse4();
16729
16732
  if ([".app.json", ".mcp.json"].includes(base) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
16730
16733
  refuse4();
16731
- if (parts.includes("hooks") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base !== "hooks" : base !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join20(cacheRoot, parts[0], parts[1]))))
16734
+ if ((parts[3] === "hooks" || base === "hooks.json") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base !== "hooks" : base !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join20(cacheRoot, parts[0], parts[1]))))
16732
16735
  refuse4();
16733
16736
  }
16734
16737
  }
@@ -21095,9 +21098,7 @@ var GENERIC_ENV_PATTERN = /\b((?:OPENAI|ANTHROPIC|GEMINI|XAI|ELEVENLABS|DEEPGRAM
21095
21098
  function extractEnvVars(text2) {
21096
21099
  const envVars = new Set;
21097
21100
  for (const pattern of [ENV_VAR_PATTERN, GENERIC_ENV_PATTERN]) {
21098
- pattern.lastIndex = 0;
21099
- let match;
21100
- while ((match = pattern.exec(text2)) !== null) {
21101
+ for (const match of text2.matchAll(pattern)) {
21101
21102
  envVars.add(match[1]);
21102
21103
  }
21103
21104
  }
@@ -23,8 +23,18 @@ export declare function getSkillBestDoc(name: string, file?: string): string | n
23
23
  * Extract requirements from a skill's source files
24
24
  */
25
25
  export declare function getSkillRequirements(name: string): SkillRequirements | null;
26
- /** Shared parser for verified bundle content and explicit local authoring files. */
27
- export declare function getSkillRequirementsFromContent(name: string, texts: string[], dependencies?: Record<string, string>, meta?: SkillMeta): SkillRequirements;
26
+ /**
27
+ * Shared parser for verified bundle content and explicit local authoring files.
28
+ * Public through `@hasna/skills/sdk`.
29
+ *
30
+ * The result depends only on the arguments: `texts` are the documentation
31
+ * contents to scan (for example SKILL.md, README.md, CLAUDE.md, .env.example),
32
+ * `dependencies` is the skill package's dependency map and `meta` its catalog
33
+ * record. It reads no registry, file, environment, install state or network,
34
+ * and never mutates its inputs; the returned `dependencies` is the caller's
35
+ * object, not a copy.
36
+ */
37
+ export declare function getSkillRequirementsFromContent(name: string, texts: readonly string[], dependencies?: Record<string, string>, meta?: SkillMeta): SkillRequirements;
28
38
  export interface SkillDependencyStatus {
29
39
  name: string;
30
40
  version: string;
@@ -49,7 +49,7 @@ var __require = import.meta.require;
49
49
  // src/runtime-prerequisites.ts
50
50
  import { isAbsolute as isAbsolute5 } from "path";
51
51
  // package.json
52
- var version = "0.10.66";
52
+ var version = "0.10.68";
53
53
  var package_default = {
54
54
  name: "@hasna/skills",
55
55
  version,
@@ -1937,7 +1937,8 @@ var MEASURED_CODEX_CAPABILITIES = {
1937
1937
  "codex-cli 0.160.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
1938
1938
  "codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
1939
1939
  "codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
1940
- "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
1940
+ "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
1941
+ "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
1941
1942
  };
1942
1943
  function supportsCodexNativeCapability(version2, capability) {
1943
1944
  return typeof version2 === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version2) && MEASURED_CODEX_CAPABILITIES[version2].includes(capability);
@@ -44,6 +44,7 @@ export * from "../lib/remote-recurring-contract.js";
44
44
  export { RecurringInputError, RemoteRecurringError, RemoteRecurringUnavailableError, RemoteRecurringReadError, RemoteRecurringUnconfirmedError } from "../lib/remote-recurring.js";
45
45
  export type { RecurringCapability, RecurringListOptions, RemoteRecurringErrorCode } from "../lib/remote-recurring.js";
46
46
  export { canonicalJson, canonicalJsonSha256 } from "../lib/canonical-json.js";
47
+ export { getSkillRequirementsFromContent, type SkillRequirements } from "../lib/skillinfo.js";
47
48
  export type { RemoteQuoteUnavailableCode } from "../lib/remote-client.js";
48
49
  export { RemoteCreditApprovalError, type RemoteRunApproval, type RemoteRunQuote, type RemoteCreditPack } from "../lib/remote-account.js";
49
50
  export { type RemoteInputFile, type RemoteInputFileDescriptor } from "../lib/remote-files.js";
package/dist/sdk/index.js CHANGED
@@ -32371,7 +32371,7 @@ var init_event_sink = __esm(() => {
32371
32371
  init_dist();
32372
32372
  });
32373
32373
  // package.json
32374
- var version = "0.10.66";
32374
+ var version = "0.10.68";
32375
32375
  var package_default = {
32376
32376
  name: "@hasna/skills",
32377
32377
  version,
@@ -38807,7 +38807,8 @@ var MEASURED_CODEX_CAPABILITIES = {
38807
38807
  "codex-cli 0.160.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
38808
38808
  "codex-cli 0.160.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
38809
38809
  "codex-cli 0.161.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
38810
- "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
38810
+ "codex-cli 0.162.0": ["hooks", "qualified-skill-catalog", "installed-plugin-review"],
38811
+ "codex-cli 0.162.1": ["hooks", "qualified-skill-catalog", "installed-plugin-review"]
38811
38812
  };
38812
38813
  function supportsCodexNativeCapability(version2, capability) {
38813
38814
  return typeof version2 === "string" && Object.hasOwn(MEASURED_CODEX_CAPABILITIES, version2) && MEASURED_CODEX_CAPABILITIES[version2].includes(capability);
@@ -40684,7 +40685,9 @@ function codexLocalMarketplaceMapsPlugin(text, marketplace, plugin) {
40684
40685
  try {
40685
40686
  hashNativeJsonControls(text);
40686
40687
  const value = JSON.parse(text);
40687
- if (!plainObject(value) || !exactKeys2(value, ["interface", "name", "plugins"]) || value.name !== marketplace || !(typeof value.interface === "string" || Number.isSafeInteger(value.interface)) || !Array.isArray(value.plugins) || value.plugins.length < 1 || value.plugins.length > 4096)
40688
+ const marketplaceInterface = plainObject(value) ? value.interface : undefined;
40689
+ const validInterface = typeof marketplaceInterface === "string" || Number.isSafeInteger(marketplaceInterface) || plainObject(marketplaceInterface) && exactKeys2(marketplaceInterface, [], ["displayName"]) && typeof marketplaceInterface.displayName === "string" && marketplaceInterface.displayName.trim().length > 0;
40690
+ if (!plainObject(value) || !exactKeys2(value, ["interface", "name", "plugins"]) || value.name !== marketplace || !validInterface || !Array.isArray(value.plugins) || value.plugins.length < 1 || value.plugins.length > 4096)
40688
40691
  return false;
40689
40692
  const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
40690
40693
  if (rows.length !== 1)
@@ -41060,7 +41063,7 @@ function reviewedHookControls(root, read) {
41060
41063
  if (!plainObject(manifest) || !exactKeys2(manifest, ["name", "version", "hooks"], ["description"]) || !identifier4(manifest.name) || typeof manifest.version !== "string" || manifest.version !== basename5(root) || manifest.hooks !== "./hooks/hooks.json" || manifest.description !== undefined && (typeof manifest.description !== "string" || !manifest.description.trim() || manifest.description.length > 1024))
41061
41064
  refuse5();
41062
41065
  const manifestRecord = manifest;
41063
- if (!plainObject(hooks) || !exactKeys2(hooks, ["hooks"]) || !plainObject(hooks.hooks))
41066
+ if (!plainObject(hooks) || !exactKeys2(hooks, ["hooks"], ["description"]) || !plainObject(hooks.hooks) || hooks.description !== undefined && typeof hooks.description !== "string")
41064
41067
  refuse5();
41065
41068
  const hookTable = hooks.hooks;
41066
41069
  const events = Object.entries(hookTable);
@@ -41187,7 +41190,7 @@ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files,
41187
41190
  refuse5();
41188
41191
  if ([".app.json", ".mcp.json"].includes(base) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
41189
41192
  refuse5();
41190
- if (parts.includes("hooks") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base !== "hooks" : base !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join15(cacheRoot, parts[0], parts[1]))))
41193
+ if ((parts[3] === "hooks" || base === "hooks.json") && (![4, 5].includes(parts.length) || parts[3] !== "hooks" || (parts.length === 4 ? base !== "hooks" : base !== "hooks.json") || !hookParents.some((reviewed) => reviewed.pluginParent === join15(cacheRoot, parts[0], parts[1]))))
41191
41194
  refuse5();
41192
41195
  }
41193
41196
  }
@@ -51554,9 +51557,7 @@ var GENERIC_ENV_PATTERN = /\b((?:OPENAI|ANTHROPIC|GEMINI|XAI|ELEVENLABS|DEEPGRAM
51554
51557
  function extractEnvVars(text2) {
51555
51558
  const envVars = new Set;
51556
51559
  for (const pattern of [ENV_VAR_PATTERN, GENERIC_ENV_PATTERN]) {
51557
- pattern.lastIndex = 0;
51558
- let match;
51559
- while ((match = pattern.exec(text2)) !== null) {
51560
+ for (const match of text2.matchAll(pattern)) {
51560
51561
  envVars.add(match[1]);
51561
51562
  }
51562
51563
  }
@@ -97724,6 +97725,7 @@ export {
97724
97725
  inspectSkillSession,
97725
97726
  inspectSkillBundle,
97726
97727
  inspectPrivatePublication,
97728
+ getSkillRequirementsFromContent,
97727
97729
  getServerSkillMd,
97728
97730
  getServerSkill,
97729
97731
  expiresAtFor,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/skills",
3
- "version": "0.10.66",
3
+ "version": "0.10.68",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {