@hasna/skills 0.1.32 → 0.1.34

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 (95) hide show
  1. package/README.md +1 -16
  2. package/bin/index.js +57 -14
  3. package/bin/mcp.js +57 -13
  4. package/dist/cli/commands/runtime.d.ts +1 -1
  5. package/dist/index.js +51 -5
  6. package/dist/lib/skill-validation.d.ts +2 -0
  7. package/package.json +1 -1
  8. package/skills/ad-creative-pack/package.json +4 -7
  9. package/skills/api-docs-portal/package.json +5 -3
  10. package/skills/audio/package.json +4 -22
  11. package/skills/audio-transcript-pack/package.json +3 -2
  12. package/skills/blog-article/package.json +5 -21
  13. package/skills/brand-kit/package.json +4 -7
  14. package/skills/brand-photo-shoot/package.json +3 -2
  15. package/skills/browse/README.md +13 -256
  16. package/skills/browse/SKILL.md +24 -45
  17. package/skills/browse/package.json +4 -33
  18. package/skills/contract-review-report/package.json +3 -2
  19. package/skills/customer-feedback-report/package.json +3 -2
  20. package/skills/deepresearch/CLAUDE.md +6 -39
  21. package/skills/deepresearch/README.md +15 -150
  22. package/skills/deepresearch/SKILL.md +23 -32
  23. package/skills/deepresearch/package.json +4 -37
  24. package/skills/domainpurchase/SKILL.md +14 -31
  25. package/skills/domainpurchase/package.json +3 -8
  26. package/skills/email-sequence/package.json +4 -7
  27. package/skills/generate-book-cover/package.json +5 -23
  28. package/skills/icon-pack/package.json +4 -22
  29. package/skills/image/README.md +13 -183
  30. package/skills/image/package.json +4 -22
  31. package/skills/invoice-reconciliation/package.json +3 -2
  32. package/skills/landing-page-pack/package.json +4 -7
  33. package/skills/logo-design/package.json +4 -2
  34. package/skills/managemcp/CLAUDE.md +9 -43
  35. package/skills/managemcp/README.md +12 -198
  36. package/skills/managemcp/package.json +2 -10
  37. package/skills/managemcp/tsconfig.json +1 -3
  38. package/skills/market-research-report/package.json +5 -4
  39. package/skills/meeting-pack/package.json +3 -2
  40. package/skills/migration-plan-pack/package.json +3 -2
  41. package/skills/music/package.json +4 -20
  42. package/skills/music-album/package.json +3 -2
  43. package/skills/one-page-website/package.json +3 -2
  44. package/skills/pdf-read/package.json +5 -23
  45. package/skills/pdf-to-dataset/package.json +5 -4
  46. package/skills/pdf-to-markdown/package.json +5 -4
  47. package/skills/performance-audit-report/package.json +3 -2
  48. package/skills/photo-album/package.json +4 -2
  49. package/skills/pitch-deck/package.json +5 -3
  50. package/skills/playlist-maker/package.json +4 -2
  51. package/skills/product-mockup/package.json +3 -2
  52. package/skills/proposal-pack/package.json +5 -4
  53. package/skills/read-pdf/package.json +4 -29
  54. package/skills/remove-background/package.json +5 -21
  55. package/skills/repo-onboarding-report/package.json +3 -2
  56. package/skills/sdk-generator/package.json +3 -2
  57. package/skills/security-audit-report/package.json +5 -4
  58. package/skills/seo-content-pack/package.json +5 -3
  59. package/skills/short-video-pack/package.json +3 -2
  60. package/skills/slide-deck-generator/package.json +3 -2
  61. package/skills/sms/README.md +18 -56
  62. package/skills/sms/package.json +1 -3
  63. package/skills/sms/tsconfig.json +1 -2
  64. package/skills/social-content-calendar/package.json +4 -7
  65. package/skills/test-suite-generator/package.json +5 -4
  66. package/skills/transcript/package.json +4 -23
  67. package/skills/video/package.json +4 -24
  68. package/skills/video-highlight-pack/package.json +3 -2
  69. package/skills/voiceover-jingle-pack/package.json +3 -2
  70. package/skills/webcrawling/CLAUDE.md +6 -17
  71. package/skills/webcrawling/README.md +13 -66
  72. package/skills/webcrawling/package.json +4 -32
  73. package/skills/browse/.env.example +0 -14
  74. package/skills/deepresearch/.env.example +0 -9
  75. package/skills/domainpurchase/.env.example +0 -7
  76. package/skills/domainpurchase/src/lib/api-client.ts +0 -571
  77. package/skills/domainpurchase/src/lib/config.ts +0 -209
  78. package/skills/domainpurchase/src/lib/installer.ts +0 -165
  79. package/skills/image/.env.example +0 -12
  80. package/skills/image/PROJECT_OVERVIEW.md +0 -216
  81. package/skills/image/QUICKSTART.md +0 -111
  82. package/skills/managemcp/.env.example +0 -16
  83. package/skills/managemcp/scripts/migrate.ts +0 -57
  84. package/skills/managemcp/scripts/seed.ts +0 -58
  85. package/skills/managemcp/src/db/index.ts +0 -37
  86. package/skills/managemcp/src/lib/api-client.ts +0 -108
  87. package/skills/managemcp/src/lib/config.ts +0 -34
  88. package/skills/managemcp/src/lib/installer.ts +0 -140
  89. package/skills/managemcp/src/lib/service-dir.ts +0 -119
  90. package/skills/sms/scripts/buy-number.ts +0 -63
  91. package/skills/sms/scripts/test-send.ts +0 -67
  92. package/skills/sms/src/server.ts +0 -351
  93. package/skills/sms/src/sse-server.ts +0 -279
  94. package/skills/transcript/.env.example +0 -10
  95. package/skills/webcrawling/.env.example +0 -2
package/README.md CHANGED
@@ -113,7 +113,6 @@ requirements explicitly document local provider use.
113
113
  | `skills schedule list` | | List all schedules (enabled/disabled/last run) |
114
114
  | `skills mcp` | | Start MCP server on stdio |
115
115
  | `skills mcp --register claude` | | Register the Skills MCP server in an agent config (also `codex`, `gemini`, `opencode`, `all`) |
116
- | `skills serve` | | Start the HTTP dashboard on localhost |
117
116
  | `skills self-update` | | Update this package to the latest version |
118
117
  | `skills completion <shell>` | | Generate shell completions (bash, zsh, fish) |
119
118
 
@@ -224,22 +223,12 @@ Hosted account, billing, and credit management use the configured hosted API.
224
223
  The public package only stores local configuration and CLI credentials; Stripe,
225
224
  customer records, and hosted execution remain platform concerns.
226
225
 
227
- ## Dashboard
228
-
229
- ```bash
230
- skills serve # Start HTTP server (opens browser automatically)
231
- skills serve --no-open # Start without opening the browser
232
- ```
233
-
234
- Dashboard features: searchable/filterable skills table, detail dialogs, stats cards, dark/light/system theme, oklch color tokens.
235
-
236
226
  ## Project Structure
237
227
 
238
228
  ```
239
229
  src/
240
230
  ├── cli/index.tsx # Commander.js CLI + Ink TUI
241
231
  ├── mcp/index.ts # MCP server (stdio) with ~20 tools
242
- ├── server/serve.ts # Bun.serve HTTP server + REST API
243
232
  ├── lib/
244
233
  │ ├── registry.ts # 202+ entries, search, categories, tags
245
234
  │ ├── installer.ts # Project pins and disabled source-copy paths
@@ -254,9 +243,7 @@ src/
254
243
 
255
244
  skills/ # 202+ public skill contracts and local OSS skills
256
245
  ├── _common/ # Shared utilities
257
- └── */ # Local skills include src/; premium package output excludes private src/
258
-
259
- dashboard/ # Next.js + React 19 + Tailwind v4 app
246
+ └── */ # Local skills include src/; hosted skills are metadata-only
260
247
  ```
261
248
 
262
249
  ## Project Runtime State
@@ -283,8 +270,6 @@ belong in `~/.cache/skills` or the remote API, not inside project `.skills`.
283
270
  bun install
284
271
  bun run build # Build CLI, MCP, library, and types
285
272
  bun run dev # Run CLI in dev mode (no build needed)
286
- bun run dashboard:dev # Vite dev server for web dashboard
287
- bun run server:dev # HTTP server with --watch
288
273
  bun test # Run all tests
289
274
  bun run typecheck # TypeScript type checking
290
275
  ```
package/bin/index.js CHANGED
@@ -1910,7 +1910,7 @@ var package_default;
1910
1910
  var init_package = __esm(() => {
1911
1911
  package_default = {
1912
1912
  name: "@hasna/skills",
1913
- version: "0.1.32",
1913
+ version: "0.1.34",
1914
1914
  description: "Skills library for AI coding agents",
1915
1915
  type: "module",
1916
1916
  bin: {
@@ -6648,7 +6648,13 @@ function formatCost(cents) {
6648
6648
  return `$${(cents / 100).toFixed(2)}`;
6649
6649
  }
6650
6650
  function getAllPremiumSlugs() {
6651
- return [...PREMIUM_SKILLS.map((s) => s.slug), ARTICLE_GENERATION_SLUG];
6651
+ return [
6652
+ ...new Set([
6653
+ ...PREMIUM_SKILLS.map((skill) => skill.slug),
6654
+ ...MEDIA_GENERATION_PRICES.map((price) => price.slug),
6655
+ ARTICLE_GENERATION_SLUG
6656
+ ])
6657
+ ].sort();
6652
6658
  }
6653
6659
  var MUSIC_ALBUM_SLUG = "music-album", MUSIC_ALBUM_SONG_COUNTS, MUSIC_ALBUM_COST_CENTS_PER_SONG = 150, PREMIUM_SKILLS, ARTICLE_GENERATION_SLUG = "blog-article", ARTICLE_MAX_COUNT = 12, ARTICLE_COUNT_ERROR, ARTICLE_INTERNAL_COST_CENTS = 5, ARTICLE_MARKUP_MULTIPLIER = 5, ARTICLE_USER_COST_CENTS, ARTICLE_TONES, ARTICLE_LENGTHS, MEDIA_GENERATION_PRICES, premiumIndex, mediaSlugs;
6654
6660
  var init_pricing = __esm(() => {
@@ -21971,6 +21977,25 @@ function isSafeRelativePath(value) {
21971
21977
  const normalized = normalize(value).replace(/\\/g, "/");
21972
21978
  return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
21973
21979
  }
21980
+ function isHostedPackageMetadata(pkg) {
21981
+ const skills = asRecord(pkg.skills);
21982
+ if (!skills)
21983
+ return false;
21984
+ const runtime = typeof skills.runtime === "string" ? skills.runtime.trim().toLowerCase() : "";
21985
+ const source = typeof skills.source === "string" ? skills.source.trim().toLowerCase() : "";
21986
+ return runtime === "hosted" || source === "remote" || source === "private-hosted";
21987
+ }
21988
+ function isHostedMetadataSkill(skillName, frontmatter, registryMeta, packageDeclaresHosted) {
21989
+ if (packageDeclaresHosted)
21990
+ return true;
21991
+ if (isPremiumSkill(skillName))
21992
+ return true;
21993
+ if (frontmatter?.source === "private-hosted")
21994
+ return true;
21995
+ if (frontmatter?.source === "remote" && !registryMeta?.tags.includes("local"))
21996
+ return true;
21997
+ return false;
21998
+ }
21974
21999
  function parseSkillFrontmatter(content) {
21975
22000
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
21976
22001
  if (!match)
@@ -22020,6 +22045,8 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22020
22045
  const bareName = name;
22021
22046
  const issues = [];
22022
22047
  const warnings = [];
22048
+ let packageDeclaresHosted = false;
22049
+ let packageSkillSource;
22023
22050
  const metadata = {
22024
22051
  binCommands: [],
22025
22052
  docFiles: []
@@ -22102,6 +22129,13 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22102
22129
  if (!packageRecord) {
22103
22130
  add(issues, "package.invalid_shape", "package.json must be an object");
22104
22131
  } else {
22132
+ packageDeclaresHosted = isHostedPackageMetadata(pkg);
22133
+ const skillsRecord = asRecord(pkg.skills);
22134
+ if (skillsRecord && typeof skillsRecord.source === "string") {
22135
+ packageSkillSource = skillsRecord.source;
22136
+ }
22137
+ const hostedMetadata2 = isHostedMetadataSkill(bareName, metadata.skillMdFrontmatter, registryMeta, packageDeclaresHosted);
22138
+ metadata.runtime = hostedMetadata2 ? "hosted" : "local";
22105
22139
  if (typeof pkg.name === "string") {
22106
22140
  metadata.packageName = pkg.name;
22107
22141
  if (pkg.name !== bareName) {
@@ -22118,12 +22152,18 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22118
22152
  ...metadata.provenance ?? { directoryName: bareName },
22119
22153
  ...typeof pkg.name === "string" ? { packageName: pkg.name } : {},
22120
22154
  ...typeof pkg.version === "string" && pkg.version.trim() ? { packageVersion: pkg.version } : {},
22121
- ...registryMeta?.source ? { registrySource: registryMeta.source } : {}
22155
+ ...registryMeta?.source ? { registrySource: registryMeta.source } : {},
22156
+ ...packageSkillSource ? { packageSkillSource } : {}
22122
22157
  };
22123
22158
  const binRecord = asRecord(pkg.bin);
22124
22159
  if (!binRecord || Object.keys(binRecord).length === 0) {
22125
- add(issues, "package.bin_missing", "package.json missing non-empty bin object");
22160
+ if (!hostedMetadata2) {
22161
+ add(issues, "package.bin_missing", "package.json missing non-empty bin object");
22162
+ }
22126
22163
  } else {
22164
+ if (hostedMetadata2) {
22165
+ add(issues, "package.hosted_bin_forbidden", "Hosted metadata packages must not expose a local bin entry");
22166
+ }
22127
22167
  for (const [command, target] of Object.entries(binRecord)) {
22128
22168
  if (!VALID_BIN_COMMAND.test(command)) {
22129
22169
  add(issues, "package.bin_command_invalid", `package.json bin command '${command}' must use lowercase letters, numbers, dots, underscores, or hyphens`);
@@ -22150,8 +22190,14 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22150
22190
  add(issues, "package.invalid_json", `package.json is invalid JSON: ${error48.message}`);
22151
22191
  }
22152
22192
  }
22193
+ const hostedMetadata = isHostedMetadataSkill(bareName, metadata.skillMdFrontmatter, registryMeta, packageDeclaresHosted);
22194
+ metadata.runtime = hostedMetadata ? "hosted" : "local";
22153
22195
  const srcDir = join7(skillPath, "src");
22154
- if (!existsSync7(srcDir)) {
22196
+ if (hostedMetadata) {
22197
+ if (existsSync7(srcDir)) {
22198
+ add(issues, "skill.hosted_source_forbidden", "Hosted metadata skills must not include local implementation source");
22199
+ }
22200
+ } else if (!existsSync7(srcDir)) {
22155
22201
  add(issues, "skill.src_missing", "Missing src/ directory");
22156
22202
  } else if (!existsSync7(join7(srcDir, "index.ts")) && !existsSync7(join7(srcDir, "index.js"))) {
22157
22203
  add(issues, "skill.src_index_missing", "Missing src/index.ts or src/index.js");
@@ -22172,6 +22218,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22172
22218
  }
22173
22219
  var DOC_FILES, RESERVED_SKILL_ENTRIES, KNOWN_TOP_LEVEL_ENTRIES, VALID_PROVENANCE_SOURCES, VALID_BIN_COMMAND;
22174
22220
  var init_skill_validation = __esm(() => {
22221
+ init_pricing();
22175
22222
  DOC_FILES = ["SKILL.md", "README.md", "CLAUDE.md"];
22176
22223
  RESERVED_SKILL_ENTRIES = new Set([
22177
22224
  ".env",
@@ -39522,7 +39569,7 @@ function registerOperationTools(server) {
39522
39569
  const run = completeSkillRun(runContext, { status: "failed", error: error48, costCents });
39523
39570
  return mcpError("AUTH_REQUIRED", `${error48}. Local run metadata: ${run.paths.runDir}/run.json`, ["skills auth login"]);
39524
39571
  }
39525
- if (apiKey) {
39572
+ if (isPremiumSkill2(skillName) && apiKey) {
39526
39573
  try {
39527
39574
  const { RemoteSkillsClient: RemoteSkillsClient2 } = await Promise.resolve().then(() => (init_remote_client(), exports_remote_client));
39528
39575
  const client = new RemoteSkillsClient2(apiKey);
@@ -39552,14 +39599,11 @@ function registerOperationTools(server) {
39552
39599
  nextActions: remoteRunNextActions(remoteRunId)
39553
39600
  });
39554
39601
  } catch (err) {
39555
- console.error(`[skills] hosted API failed for ${skillName}, falling back to local:`, err.message);
39556
- if (isPremiumSkill2(skillName)) {
39557
- const error48 = `Hosted skill ${skillName} requires hosted access: ${err.message}`;
39558
- writeRunLogs(runContext, "", error48 + `
39602
+ const error48 = `Hosted skill ${skillName} requires hosted access: ${err.message}`;
39603
+ writeRunLogs(runContext, "", error48 + `
39559
39604
  `);
39560
- const localRun2 = completeSkillRun(runContext, { status: "failed", error: error48 });
39561
- return mcpError("PLATFORM_ERROR", `${error48}. Local run metadata: ${localRun2.paths.runDir}/run.json`);
39562
- }
39605
+ const localRun2 = completeSkillRun(runContext, { status: "failed", error: error48 });
39606
+ return mcpError("PLATFORM_ERROR", `${error48}. Local run metadata: ${localRun2.paths.runDir}/run.json`);
39563
39607
  }
39564
39608
  }
39565
39609
  const result = await runSkill(skillName, runArgs, {
@@ -41549,7 +41593,6 @@ var init_completion = __esm(() => {
41549
41593
  "categories",
41550
41594
  "tags",
41551
41595
  "mcp",
41552
- "serve",
41553
41596
  "self-update",
41554
41597
  "completion",
41555
41598
  "export",
package/bin/mcp.js CHANGED
@@ -6921,7 +6921,13 @@ function formatCost(cents) {
6921
6921
  return `$${(cents / 100).toFixed(2)}`;
6922
6922
  }
6923
6923
  function getAllPremiumSlugs() {
6924
- return [...PREMIUM_SKILLS.map((s) => s.slug), ARTICLE_GENERATION_SLUG];
6924
+ return [
6925
+ ...new Set([
6926
+ ...PREMIUM_SKILLS.map((skill) => skill.slug),
6927
+ ...MEDIA_GENERATION_PRICES.map((price) => price.slug),
6928
+ ARTICLE_GENERATION_SLUG
6929
+ ])
6930
+ ].sort();
6925
6931
  }
6926
6932
  var MUSIC_ALBUM_SLUG = "music-album", MUSIC_ALBUM_SONG_COUNTS, MUSIC_ALBUM_COST_CENTS_PER_SONG = 150, PREMIUM_SKILLS, ARTICLE_GENERATION_SLUG = "blog-article", ARTICLE_MAX_COUNT = 12, ARTICLE_COUNT_ERROR, ARTICLE_INTERNAL_COST_CENTS = 5, ARTICLE_MARKUP_MULTIPLIER = 5, ARTICLE_USER_COST_CENTS, ARTICLE_TONES, ARTICLE_LENGTHS, MEDIA_GENERATION_PRICES, premiumIndex, mediaSlugs;
6927
6933
  var init_pricing = __esm(() => {
@@ -21787,7 +21793,7 @@ class StdioServerTransport {
21787
21793
  // package.json
21788
21794
  var package_default = {
21789
21795
  name: "@hasna/skills",
21790
- version: "0.1.32",
21796
+ version: "0.1.34",
21791
21797
  description: "Skills library for AI coding agents",
21792
21798
  type: "module",
21793
21799
  bin: {
@@ -33296,7 +33302,7 @@ function registerOperationTools(server) {
33296
33302
  const run = completeSkillRun(runContext, { status: "failed", error: error48, costCents });
33297
33303
  return mcpError("AUTH_REQUIRED", `${error48}. Local run metadata: ${run.paths.runDir}/run.json`, ["skills auth login"]);
33298
33304
  }
33299
- if (apiKey) {
33305
+ if (isPremiumSkill2(skillName) && apiKey) {
33300
33306
  try {
33301
33307
  const { RemoteSkillsClient: RemoteSkillsClient2 } = await Promise.resolve().then(() => (init_remote_client(), exports_remote_client));
33302
33308
  const client = new RemoteSkillsClient2(apiKey);
@@ -33326,14 +33332,11 @@ function registerOperationTools(server) {
33326
33332
  nextActions: remoteRunNextActions(remoteRunId)
33327
33333
  });
33328
33334
  } catch (err) {
33329
- console.error(`[skills] hosted API failed for ${skillName}, falling back to local:`, err.message);
33330
- if (isPremiumSkill2(skillName)) {
33331
- const error48 = `Hosted skill ${skillName} requires hosted access: ${err.message}`;
33332
- writeRunLogs(runContext, "", error48 + `
33335
+ const error48 = `Hosted skill ${skillName} requires hosted access: ${err.message}`;
33336
+ writeRunLogs(runContext, "", error48 + `
33333
33337
  `);
33334
- const localRun2 = completeSkillRun(runContext, { status: "failed", error: error48 });
33335
- return mcpError("PLATFORM_ERROR", `${error48}. Local run metadata: ${localRun2.paths.runDir}/run.json`);
33336
- }
33338
+ const localRun2 = completeSkillRun(runContext, { status: "failed", error: error48 });
33339
+ return mcpError("PLATFORM_ERROR", `${error48}. Local run metadata: ${localRun2.paths.runDir}/run.json`);
33337
33340
  }
33338
33341
  }
33339
33342
  const result = await runSkill(skillName, runArgs, {
@@ -33818,6 +33821,7 @@ function removeSchedule(idOrName, targetDir) {
33818
33821
  }
33819
33822
 
33820
33823
  // src/lib/skill-validation.ts
33824
+ init_pricing();
33821
33825
  import { existsSync as existsSync11, lstatSync, readFileSync as readFileSync9, readdirSync as readdirSync4, statSync as statSync3 } from "fs";
33822
33826
  import { isAbsolute, join as join11, normalize } from "path";
33823
33827
  var DOC_FILES = ["SKILL.md", "README.md", "CLAUDE.md"];
@@ -33882,6 +33886,25 @@ function isSafeRelativePath(value) {
33882
33886
  const normalized = normalize(value).replace(/\\/g, "/");
33883
33887
  return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
33884
33888
  }
33889
+ function isHostedPackageMetadata(pkg) {
33890
+ const skills = asRecord(pkg.skills);
33891
+ if (!skills)
33892
+ return false;
33893
+ const runtime = typeof skills.runtime === "string" ? skills.runtime.trim().toLowerCase() : "";
33894
+ const source = typeof skills.source === "string" ? skills.source.trim().toLowerCase() : "";
33895
+ return runtime === "hosted" || source === "remote" || source === "private-hosted";
33896
+ }
33897
+ function isHostedMetadataSkill(skillName, frontmatter, registryMeta, packageDeclaresHosted) {
33898
+ if (packageDeclaresHosted)
33899
+ return true;
33900
+ if (isPremiumSkill(skillName))
33901
+ return true;
33902
+ if (frontmatter?.source === "private-hosted")
33903
+ return true;
33904
+ if (frontmatter?.source === "remote" && !registryMeta?.tags.includes("local"))
33905
+ return true;
33906
+ return false;
33907
+ }
33885
33908
  function parseSkillFrontmatter(content) {
33886
33909
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
33887
33910
  if (!match)
@@ -33931,6 +33954,8 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
33931
33954
  const bareName = name;
33932
33955
  const issues = [];
33933
33956
  const warnings = [];
33957
+ let packageDeclaresHosted = false;
33958
+ let packageSkillSource;
33934
33959
  const metadata = {
33935
33960
  binCommands: [],
33936
33961
  docFiles: []
@@ -34013,6 +34038,13 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
34013
34038
  if (!packageRecord) {
34014
34039
  add(issues, "package.invalid_shape", "package.json must be an object");
34015
34040
  } else {
34041
+ packageDeclaresHosted = isHostedPackageMetadata(pkg);
34042
+ const skillsRecord = asRecord(pkg.skills);
34043
+ if (skillsRecord && typeof skillsRecord.source === "string") {
34044
+ packageSkillSource = skillsRecord.source;
34045
+ }
34046
+ const hostedMetadata2 = isHostedMetadataSkill(bareName, metadata.skillMdFrontmatter, registryMeta, packageDeclaresHosted);
34047
+ metadata.runtime = hostedMetadata2 ? "hosted" : "local";
34016
34048
  if (typeof pkg.name === "string") {
34017
34049
  metadata.packageName = pkg.name;
34018
34050
  if (pkg.name !== bareName) {
@@ -34029,12 +34061,18 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
34029
34061
  ...metadata.provenance ?? { directoryName: bareName },
34030
34062
  ...typeof pkg.name === "string" ? { packageName: pkg.name } : {},
34031
34063
  ...typeof pkg.version === "string" && pkg.version.trim() ? { packageVersion: pkg.version } : {},
34032
- ...registryMeta?.source ? { registrySource: registryMeta.source } : {}
34064
+ ...registryMeta?.source ? { registrySource: registryMeta.source } : {},
34065
+ ...packageSkillSource ? { packageSkillSource } : {}
34033
34066
  };
34034
34067
  const binRecord = asRecord(pkg.bin);
34035
34068
  if (!binRecord || Object.keys(binRecord).length === 0) {
34036
- add(issues, "package.bin_missing", "package.json missing non-empty bin object");
34069
+ if (!hostedMetadata2) {
34070
+ add(issues, "package.bin_missing", "package.json missing non-empty bin object");
34071
+ }
34037
34072
  } else {
34073
+ if (hostedMetadata2) {
34074
+ add(issues, "package.hosted_bin_forbidden", "Hosted metadata packages must not expose a local bin entry");
34075
+ }
34038
34076
  for (const [command, target] of Object.entries(binRecord)) {
34039
34077
  if (!VALID_BIN_COMMAND.test(command)) {
34040
34078
  add(issues, "package.bin_command_invalid", `package.json bin command '${command}' must use lowercase letters, numbers, dots, underscores, or hyphens`);
@@ -34061,8 +34099,14 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
34061
34099
  add(issues, "package.invalid_json", `package.json is invalid JSON: ${error48.message}`);
34062
34100
  }
34063
34101
  }
34102
+ const hostedMetadata = isHostedMetadataSkill(bareName, metadata.skillMdFrontmatter, registryMeta, packageDeclaresHosted);
34103
+ metadata.runtime = hostedMetadata ? "hosted" : "local";
34064
34104
  const srcDir = join11(skillPath, "src");
34065
- if (!existsSync11(srcDir)) {
34105
+ if (hostedMetadata) {
34106
+ if (existsSync11(srcDir)) {
34107
+ add(issues, "skill.hosted_source_forbidden", "Hosted metadata skills must not include local implementation source");
34108
+ }
34109
+ } else if (!existsSync11(srcDir)) {
34066
34110
  add(issues, "skill.src_missing", "Missing src/ directory");
34067
34111
  } else if (!existsSync11(join11(srcDir, "index.ts")) && !existsSync11(join11(srcDir, "index.js"))) {
34068
34112
  add(issues, "skill.src_index_missing", "Missing src/index.ts or src/index.js");
@@ -1,5 +1,5 @@
1
1
  /**
2
- * run / mcp / serve / self-update — runtime commands
2
+ * run / mcp / self-update — runtime commands
3
3
  */
4
4
  import type { Command } from "commander";
5
5
  export declare function registerRuntime(parent: Command): void;
package/dist/index.js CHANGED
@@ -3094,7 +3094,13 @@ function formatCost(cents) {
3094
3094
  return `$${(cents / 100).toFixed(2)}`;
3095
3095
  }
3096
3096
  function getAllPremiumSlugs() {
3097
- return [...PREMIUM_SKILLS.map((s) => s.slug), ARTICLE_GENERATION_SLUG];
3097
+ return [
3098
+ ...new Set([
3099
+ ...PREMIUM_SKILLS.map((skill) => skill.slug),
3100
+ ...MEDIA_GENERATION_PRICES.map((price) => price.slug),
3101
+ ARTICLE_GENERATION_SLUG
3102
+ ])
3103
+ ].sort();
3098
3104
  }
3099
3105
 
3100
3106
  // src/lib/skillinfo.ts
@@ -17756,6 +17762,25 @@ function isSafeRelativePath(value) {
17756
17762
  const normalized = normalize(value).replace(/\\/g, "/");
17757
17763
  return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
17758
17764
  }
17765
+ function isHostedPackageMetadata(pkg) {
17766
+ const skills = asRecord(pkg.skills);
17767
+ if (!skills)
17768
+ return false;
17769
+ const runtime = typeof skills.runtime === "string" ? skills.runtime.trim().toLowerCase() : "";
17770
+ const source = typeof skills.source === "string" ? skills.source.trim().toLowerCase() : "";
17771
+ return runtime === "hosted" || source === "remote" || source === "private-hosted";
17772
+ }
17773
+ function isHostedMetadataSkill(skillName, frontmatter, registryMeta, packageDeclaresHosted) {
17774
+ if (packageDeclaresHosted)
17775
+ return true;
17776
+ if (isPremiumSkill(skillName))
17777
+ return true;
17778
+ if (frontmatter?.source === "private-hosted")
17779
+ return true;
17780
+ if (frontmatter?.source === "remote" && !registryMeta?.tags.includes("local"))
17781
+ return true;
17782
+ return false;
17783
+ }
17759
17784
  function parseSkillFrontmatter(content) {
17760
17785
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
17761
17786
  if (!match)
@@ -17805,6 +17830,8 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
17805
17830
  const bareName = name;
17806
17831
  const issues = [];
17807
17832
  const warnings = [];
17833
+ let packageDeclaresHosted = false;
17834
+ let packageSkillSource;
17808
17835
  const metadata = {
17809
17836
  binCommands: [],
17810
17837
  docFiles: []
@@ -17887,6 +17914,13 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
17887
17914
  if (!packageRecord) {
17888
17915
  add(issues, "package.invalid_shape", "package.json must be an object");
17889
17916
  } else {
17917
+ packageDeclaresHosted = isHostedPackageMetadata(pkg);
17918
+ const skillsRecord = asRecord(pkg.skills);
17919
+ if (skillsRecord && typeof skillsRecord.source === "string") {
17920
+ packageSkillSource = skillsRecord.source;
17921
+ }
17922
+ const hostedMetadata2 = isHostedMetadataSkill(bareName, metadata.skillMdFrontmatter, registryMeta, packageDeclaresHosted);
17923
+ metadata.runtime = hostedMetadata2 ? "hosted" : "local";
17890
17924
  if (typeof pkg.name === "string") {
17891
17925
  metadata.packageName = pkg.name;
17892
17926
  if (pkg.name !== bareName) {
@@ -17903,12 +17937,18 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
17903
17937
  ...metadata.provenance ?? { directoryName: bareName },
17904
17938
  ...typeof pkg.name === "string" ? { packageName: pkg.name } : {},
17905
17939
  ...typeof pkg.version === "string" && pkg.version.trim() ? { packageVersion: pkg.version } : {},
17906
- ...registryMeta?.source ? { registrySource: registryMeta.source } : {}
17940
+ ...registryMeta?.source ? { registrySource: registryMeta.source } : {},
17941
+ ...packageSkillSource ? { packageSkillSource } : {}
17907
17942
  };
17908
17943
  const binRecord = asRecord(pkg.bin);
17909
17944
  if (!binRecord || Object.keys(binRecord).length === 0) {
17910
- add(issues, "package.bin_missing", "package.json missing non-empty bin object");
17945
+ if (!hostedMetadata2) {
17946
+ add(issues, "package.bin_missing", "package.json missing non-empty bin object");
17947
+ }
17911
17948
  } else {
17949
+ if (hostedMetadata2) {
17950
+ add(issues, "package.hosted_bin_forbidden", "Hosted metadata packages must not expose a local bin entry");
17951
+ }
17912
17952
  for (const [command, target] of Object.entries(binRecord)) {
17913
17953
  if (!VALID_BIN_COMMAND.test(command)) {
17914
17954
  add(issues, "package.bin_command_invalid", `package.json bin command '${command}' must use lowercase letters, numbers, dots, underscores, or hyphens`);
@@ -17935,8 +17975,14 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
17935
17975
  add(issues, "package.invalid_json", `package.json is invalid JSON: ${error48.message}`);
17936
17976
  }
17937
17977
  }
17978
+ const hostedMetadata = isHostedMetadataSkill(bareName, metadata.skillMdFrontmatter, registryMeta, packageDeclaresHosted);
17979
+ metadata.runtime = hostedMetadata ? "hosted" : "local";
17938
17980
  const srcDir = join9(skillPath, "src");
17939
- if (!existsSync9(srcDir)) {
17981
+ if (hostedMetadata) {
17982
+ if (existsSync9(srcDir)) {
17983
+ add(issues, "skill.hosted_source_forbidden", "Hosted metadata skills must not include local implementation source");
17984
+ }
17985
+ } else if (!existsSync9(srcDir)) {
17940
17986
  add(issues, "skill.src_missing", "Missing src/ directory");
17941
17987
  } else if (!existsSync9(join9(srcDir, "index.ts")) && !existsSync9(join9(srcDir, "index.js"))) {
17942
17988
  add(issues, "skill.src_index_missing", "Missing src/index.ts or src/index.js");
@@ -17985,7 +18031,7 @@ import { dirname as dirname3, relative as relative2 } from "path";
17985
18031
  // package.json
17986
18032
  var package_default = {
17987
18033
  name: "@hasna/skills",
17988
- version: "0.1.32",
18034
+ version: "0.1.34",
17989
18035
  description: "Skills library for AI coding agents",
17990
18036
  type: "module",
17991
18037
  bin: {
@@ -16,6 +16,7 @@ export interface SkillValidationResult {
16
16
  docFiles: string[];
17
17
  skillMdFrontmatter?: SkillFrontmatter;
18
18
  provenance?: SkillValidationProvenance;
19
+ runtime?: "local" | "hosted";
19
20
  };
20
21
  }
21
22
  export interface RegistryConsistencyResult {
@@ -39,6 +40,7 @@ export interface SkillValidationProvenance {
39
40
  packageVersion?: string;
40
41
  frontmatterSource?: string;
41
42
  registrySource?: string;
43
+ packageSkillSource?: string;
42
44
  }
43
45
  export declare function parseSkillFrontmatter(content: string): SkillFrontmatter | null;
44
46
  export declare function validateSkillDirectory(name: string, skillPath: string, registryMeta?: SkillMeta): SkillValidationResult;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/skills",
3
- "version": "0.1.32",
3
+ "version": "0.1.34",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "ad-creative-pack",
3
3
  "version": "0.1.0",
4
+ "private": true,
4
5
  "description": "Hosted premium paid ad creative artifact generator",
5
6
  "type": "module",
6
- "bin": {
7
- "ad-creative-pack": "./src/index.ts"
8
- },
9
- "dependencies": {},
10
- "devDependencies": {
11
- "@types/bun": "latest",
12
- "typescript": "^5.7.0"
7
+ "skills": {
8
+ "runtime": "hosted",
9
+ "source": "remote"
13
10
  }
14
11
  }
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "api-docs-portal",
3
3
  "version": "0.1.0",
4
- "type": "module",
5
4
  "private": true,
6
- "bin": {
7
- "api-docs-portal": "src/index.ts"
5
+ "description": "Hosted skill metadata for api-docs-portal",
6
+ "type": "module",
7
+ "skills": {
8
+ "runtime": "hosted",
9
+ "source": "remote"
8
10
  }
9
11
  }
@@ -1,29 +1,11 @@
1
1
  {
2
2
  "name": "audio",
3
3
  "version": "1.0.0",
4
+ "private": true,
4
5
  "description": "Generate speech and audio using provider-cost AI backends",
5
- "main": "src/index.ts",
6
6
  "type": "module",
7
- "bin": {
8
- "audio": "src/index.ts"
9
- },
10
- "scripts": {
11
- "start": "bun run src/index.ts",
12
- "generate": "bun run src/index.ts generate"
13
- },
14
- "keywords": [
15
- "audio-generation",
16
- "speech",
17
- "tts",
18
- "ai",
19
- "openai",
20
- "minimax",
21
- "gemini"
22
- ],
23
- "devDependencies": {
24
- "@types/bun": "latest"
25
- },
26
- "peerDependencies": {
27
- "typescript": "^5.0.0"
7
+ "skills": {
8
+ "runtime": "hosted",
9
+ "source": "remote"
28
10
  }
29
11
  }
@@ -4,7 +4,8 @@
4
4
  "private": true,
5
5
  "description": "Hosted premium audio transcript package generator",
6
6
  "type": "module",
7
- "bin": {
8
- "audio-transcript-pack": "./src/index.ts"
7
+ "skills": {
8
+ "runtime": "hosted",
9
+ "source": "remote"
9
10
  }
10
11
  }
@@ -1,27 +1,11 @@
1
1
  {
2
2
  "name": "blog-article",
3
3
  "version": "0.1.0",
4
+ "private": true,
4
5
  "description": "Hosted premium blog article artifact generator",
5
6
  "type": "module",
6
- "bin": {
7
- "blog-article": "./src/index.ts"
8
- },
9
- "scripts": {
10
- "dev": "bun run src/index.ts",
11
- "build": "bun build src/index.ts --outdir dist --target node",
12
- "typecheck": "tsc --noEmit"
13
- },
14
- "dependencies": {},
15
- "devDependencies": {
16
- "@types/bun": "latest",
17
- "typescript": "^5.7.0"
18
- },
19
- "publishConfig": {
20
- "access": "restricted",
21
- "registry": "https://registry.npmjs.org/"
22
- },
23
- "files": [
24
- "src",
25
- "tsconfig.json"
26
- ]
7
+ "skills": {
8
+ "runtime": "hosted",
9
+ "source": "remote"
10
+ }
27
11
  }
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "brand-kit",
3
3
  "version": "0.1.0",
4
+ "private": true,
4
5
  "description": "Hosted premium brand kit artifact generator",
5
6
  "type": "module",
6
- "bin": {
7
- "brand-kit": "./src/index.ts"
8
- },
9
- "dependencies": {},
10
- "devDependencies": {
11
- "@types/bun": "latest",
12
- "typescript": "^5.7.0"
7
+ "skills": {
8
+ "runtime": "hosted",
9
+ "source": "remote"
13
10
  }
14
11
  }
@@ -4,7 +4,8 @@
4
4
  "private": true,
5
5
  "description": "Hosted premium brand photo shoot package generator",
6
6
  "type": "module",
7
- "bin": {
8
- "brand-photo-shoot": "./src/index.ts"
7
+ "skills": {
8
+ "runtime": "hosted",
9
+ "source": "remote"
9
10
  }
10
11
  }