@hasna/skills 0.10.66 → 0.10.67

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.67",
36892
36892
  description: "Skills library for AI coding agents",
36893
36893
  type: "module",
36894
36894
  bin: {
@@ -53828,7 +53828,9 @@ function codexLocalMarketplaceMapsPlugin(text2, marketplace, plugin) {
53828
53828
  try {
53829
53829
  hashNativeJsonControls(text2);
53830
53830
  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)
53831
+ const marketplaceInterface = plainObject(value) ? value.interface : undefined;
53832
+ const validInterface = typeof marketplaceInterface === "string" || Number.isSafeInteger(marketplaceInterface) || plainObject(marketplaceInterface) && exactKeys(marketplaceInterface, [], ["displayName"]) && typeof marketplaceInterface.displayName === "string" && marketplaceInterface.displayName.trim().length > 0;
53833
+ 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
53834
  return false;
53833
53835
  const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
53834
53836
  if (rows.length !== 1)
@@ -54185,7 +54187,7 @@ function reviewedHookControls(root, read2) {
54185
54187
  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
54188
  refuse4();
54187
54189
  const manifestRecord = manifest;
54188
- if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"]) || !plainObject(hooks.hooks))
54190
+ if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"], ["description"]) || !plainObject(hooks.hooks) || hooks.description !== undefined && typeof hooks.description !== "string")
54189
54191
  refuse4();
54190
54192
  const hookTable = hooks.hooks;
54191
54193
  const events = Object.entries(hookTable);
@@ -54312,7 +54314,7 @@ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files,
54312
54314
  refuse4();
54313
54315
  if ([".app.json", ".mcp.json"].includes(base2) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
54314
54316
  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]))))
54317
+ 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
54318
  refuse4();
54317
54319
  }
54318
54320
  }
@@ -59211,9 +59213,7 @@ function generateSkillMd(name) {
59211
59213
  function extractEnvVars(text2) {
59212
59214
  const envVars = new Set;
59213
59215
  for (const pattern of [ENV_VAR_PATTERN, GENERIC_ENV_PATTERN]) {
59214
- pattern.lastIndex = 0;
59215
- let match;
59216
- while ((match = pattern.exec(text2)) !== null) {
59216
+ for (const match of text2.matchAll(pattern)) {
59217
59217
  envVars.add(match[1]);
59218
59218
  }
59219
59219
  }
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.67",
27
27
  description: "Skills library for AI coding agents",
28
28
  type: "module",
29
29
  bin: {
@@ -33706,7 +33706,9 @@ function codexLocalMarketplaceMapsPlugin(text2, marketplace, plugin) {
33706
33706
  try {
33707
33707
  hashNativeJsonControls(text2);
33708
33708
  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)
33709
+ const marketplaceInterface = plainObject(value) ? value.interface : undefined;
33710
+ const validInterface = typeof marketplaceInterface === "string" || Number.isSafeInteger(marketplaceInterface) || plainObject(marketplaceInterface) && exactKeys(marketplaceInterface, [], ["displayName"]) && typeof marketplaceInterface.displayName === "string" && marketplaceInterface.displayName.trim().length > 0;
33711
+ 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
33712
  return false;
33711
33713
  const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
33712
33714
  if (rows.length !== 1)
@@ -34063,7 +34065,7 @@ function reviewedHookControls(root, read) {
34063
34065
  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
34066
  refuse3();
34065
34067
  const manifestRecord = manifest;
34066
- if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"]) || !plainObject(hooks.hooks))
34068
+ if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"], ["description"]) || !plainObject(hooks.hooks) || hooks.description !== undefined && typeof hooks.description !== "string")
34067
34069
  refuse3();
34068
34070
  const hookTable = hooks.hooks;
34069
34071
  const events = Object.entries(hookTable);
@@ -34190,7 +34192,7 @@ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files,
34190
34192
  refuse3();
34191
34193
  if ([".app.json", ".mcp.json"].includes(base) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
34192
34194
  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]))))
34195
+ 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
34196
  refuse3();
34195
34197
  }
34196
34198
  }
@@ -35475,9 +35477,7 @@ function detectProjectSkills(cwd = process.cwd()) {
35475
35477
  function extractEnvVars(text2) {
35476
35478
  const envVars = new Set;
35477
35479
  for (const pattern of [ENV_VAR_PATTERN, GENERIC_ENV_PATTERN]) {
35478
- pattern.lastIndex = 0;
35479
- let match;
35480
- while ((match = pattern.exec(text2)) !== null) {
35480
+ for (const match of text2.matchAll(pattern)) {
35481
35481
  envVars.add(match[1]);
35482
35482
  }
35483
35483
  }
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.67",
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.67",
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.67",
24147
24147
  description: "Skills library for AI coding agents",
24148
24148
  type: "module",
24149
24149
  bin: {
package/dist/index.js CHANGED
@@ -11922,7 +11922,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
11922
11922
  });
11923
11923
  }
11924
11924
  // package.json
11925
- var version = "0.10.66";
11925
+ var version = "0.10.67";
11926
11926
  var package_default = {
11927
11927
  name: "@hasna/skills",
11928
11928
  version,
@@ -16225,7 +16225,9 @@ function codexLocalMarketplaceMapsPlugin(text2, marketplace, plugin) {
16225
16225
  try {
16226
16226
  hashNativeJsonControls(text2);
16227
16227
  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)
16228
+ const marketplaceInterface = plainObject(value) ? value.interface : undefined;
16229
+ const validInterface = typeof marketplaceInterface === "string" || Number.isSafeInteger(marketplaceInterface) || plainObject(marketplaceInterface) && exactKeys(marketplaceInterface, [], ["displayName"]) && typeof marketplaceInterface.displayName === "string" && marketplaceInterface.displayName.trim().length > 0;
16230
+ 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
16231
  return false;
16230
16232
  const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
16231
16233
  if (rows.length !== 1)
@@ -16601,7 +16603,7 @@ function reviewedHookControls(root, read2) {
16601
16603
  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
16604
  refuse4();
16603
16605
  const manifestRecord = manifest;
16604
- if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"]) || !plainObject(hooks.hooks))
16606
+ if (!plainObject(hooks) || !exactKeys(hooks, ["hooks"], ["description"]) || !plainObject(hooks.hooks) || hooks.description !== undefined && typeof hooks.description !== "string")
16605
16607
  refuse4();
16606
16608
  const hookTable = hooks.hooks;
16607
16609
  const events = Object.entries(hookTable);
@@ -16728,7 +16730,7 @@ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files,
16728
16730
  refuse4();
16729
16731
  if ([".app.json", ".mcp.json"].includes(base) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
16730
16732
  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]))))
16733
+ 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
16734
  refuse4();
16733
16735
  }
16734
16736
  }
@@ -21095,9 +21097,7 @@ var GENERIC_ENV_PATTERN = /\b((?:OPENAI|ANTHROPIC|GEMINI|XAI|ELEVENLABS|DEEPGRAM
21095
21097
  function extractEnvVars(text2) {
21096
21098
  const envVars = new Set;
21097
21099
  for (const pattern of [ENV_VAR_PATTERN, GENERIC_ENV_PATTERN]) {
21098
- pattern.lastIndex = 0;
21099
- let match;
21100
- while ((match = pattern.exec(text2)) !== null) {
21100
+ for (const match of text2.matchAll(pattern)) {
21101
21101
  envVars.add(match[1]);
21102
21102
  }
21103
21103
  }
@@ -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.67";
53
53
  var package_default = {
54
54
  name: "@hasna/skills",
55
55
  version,
@@ -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.67";
32375
32375
  var package_default = {
32376
32376
  name: "@hasna/skills",
32377
32377
  version,
@@ -40684,7 +40684,9 @@ function codexLocalMarketplaceMapsPlugin(text, marketplace, plugin) {
40684
40684
  try {
40685
40685
  hashNativeJsonControls(text);
40686
40686
  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)
40687
+ const marketplaceInterface = plainObject(value) ? value.interface : undefined;
40688
+ const validInterface = typeof marketplaceInterface === "string" || Number.isSafeInteger(marketplaceInterface) || plainObject(marketplaceInterface) && exactKeys2(marketplaceInterface, [], ["displayName"]) && typeof marketplaceInterface.displayName === "string" && marketplaceInterface.displayName.trim().length > 0;
40689
+ 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
40690
  return false;
40689
40691
  const rows = value.plugins.filter((row2) => plainObject(row2) && row2.name === plugin);
40690
40692
  if (rows.length !== 1)
@@ -41060,7 +41062,7 @@ function reviewedHookControls(root, read) {
41060
41062
  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
41063
  refuse5();
41062
41064
  const manifestRecord = manifest;
41063
- if (!plainObject(hooks) || !exactKeys2(hooks, ["hooks"]) || !plainObject(hooks.hooks))
41065
+ if (!plainObject(hooks) || !exactKeys2(hooks, ["hooks"], ["description"]) || !plainObject(hooks.hooks) || hooks.description !== undefined && typeof hooks.description !== "string")
41064
41066
  refuse5();
41065
41067
  const hookTable = hooks.hooks;
41066
41068
  const events = Object.entries(hookTable);
@@ -41187,7 +41189,7 @@ function assertParents(cacheRoot, controls, appOnlyParents, hookParents, files,
41187
41189
  refuse5();
41188
41190
  if ([".app.json", ".mcp.json"].includes(base) && hookParents.some((reviewed) => reviewed.pluginParent === parent))
41189
41191
  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]))))
41192
+ 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
41193
  refuse5();
41192
41194
  }
41193
41195
  }
@@ -51554,9 +51556,7 @@ var GENERIC_ENV_PATTERN = /\b((?:OPENAI|ANTHROPIC|GEMINI|XAI|ELEVENLABS|DEEPGRAM
51554
51556
  function extractEnvVars(text2) {
51555
51557
  const envVars = new Set;
51556
51558
  for (const pattern of [ENV_VAR_PATTERN, GENERIC_ENV_PATTERN]) {
51557
- pattern.lastIndex = 0;
51558
- let match;
51559
- while ((match = pattern.exec(text2)) !== null) {
51559
+ for (const match of text2.matchAll(pattern)) {
51560
51560
  envVars.add(match[1]);
51561
51561
  }
51562
51562
  }
@@ -97724,6 +97724,7 @@ export {
97724
97724
  inspectSkillSession,
97725
97725
  inspectSkillBundle,
97726
97726
  inspectPrivatePublication,
97727
+ getSkillRequirementsFromContent,
97727
97728
  getServerSkillMd,
97728
97729
  getServerSkill,
97729
97730
  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.67",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {