@hasna/skills 0.1.31 → 0.1.33

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 (82) hide show
  1. package/README.md +35 -30
  2. package/bin/index.js +577 -282
  3. package/bin/mcp.js +102 -39
  4. package/dist/cli/commands/runtime.d.ts +1 -1
  5. package/dist/cli/onboarding.d.ts +13 -0
  6. package/dist/index.js +88 -26
  7. package/dist/lib/config.d.ts +1 -1
  8. package/dist/lib/skill-validation.d.ts +2 -0
  9. package/package.json +1 -1
  10. package/skills/ad-creative-pack/package.json +4 -7
  11. package/skills/api-docs-portal/package.json +5 -3
  12. package/skills/audio/package.json +4 -22
  13. package/skills/audio-transcript-pack/package.json +3 -2
  14. package/skills/blog-article/package.json +5 -21
  15. package/skills/brand-kit/package.json +4 -7
  16. package/skills/brand-photo-shoot/package.json +3 -2
  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/package.json +4 -37
  21. package/skills/domainpurchase/SKILL.md +14 -31
  22. package/skills/domainpurchase/package.json +3 -8
  23. package/skills/email-sequence/package.json +4 -7
  24. package/skills/generate-book-cover/package.json +5 -23
  25. package/skills/icon-pack/package.json +4 -22
  26. package/skills/image/package.json +4 -22
  27. package/skills/invoice-reconciliation/package.json +3 -2
  28. package/skills/landing-page-pack/package.json +4 -7
  29. package/skills/logo-design/package.json +4 -2
  30. package/skills/managemcp/CLAUDE.md +9 -43
  31. package/skills/managemcp/README.md +12 -198
  32. package/skills/managemcp/package.json +2 -10
  33. package/skills/managemcp/tsconfig.json +1 -3
  34. package/skills/market-research-report/package.json +5 -4
  35. package/skills/meeting-pack/package.json +3 -2
  36. package/skills/migration-plan-pack/package.json +3 -2
  37. package/skills/music/package.json +4 -20
  38. package/skills/music-album/package.json +3 -2
  39. package/skills/one-page-website/package.json +3 -2
  40. package/skills/pdf-read/package.json +5 -23
  41. package/skills/pdf-to-dataset/package.json +5 -4
  42. package/skills/pdf-to-markdown/package.json +5 -4
  43. package/skills/performance-audit-report/package.json +3 -2
  44. package/skills/photo-album/package.json +4 -2
  45. package/skills/pitch-deck/package.json +5 -3
  46. package/skills/playlist-maker/package.json +4 -2
  47. package/skills/product-mockup/package.json +3 -2
  48. package/skills/proposal-pack/package.json +5 -4
  49. package/skills/read-pdf/package.json +4 -29
  50. package/skills/remove-background/package.json +5 -21
  51. package/skills/repo-onboarding-report/package.json +3 -2
  52. package/skills/sdk-generator/package.json +3 -2
  53. package/skills/security-audit-report/package.json +5 -4
  54. package/skills/seo-content-pack/package.json +5 -3
  55. package/skills/short-video-pack/package.json +3 -2
  56. package/skills/slide-deck-generator/package.json +3 -2
  57. package/skills/sms/README.md +18 -56
  58. package/skills/sms/package.json +1 -3
  59. package/skills/sms/tsconfig.json +1 -2
  60. package/skills/social-content-calendar/package.json +4 -7
  61. package/skills/test-suite-generator/package.json +5 -4
  62. package/skills/transcript/package.json +4 -23
  63. package/skills/video/package.json +4 -24
  64. package/skills/video-highlight-pack/package.json +3 -2
  65. package/skills/voiceover-jingle-pack/package.json +3 -2
  66. package/skills/webcrawling/package.json +4 -32
  67. package/skills/domainpurchase/.env.example +0 -7
  68. package/skills/domainpurchase/src/lib/api-client.ts +0 -571
  69. package/skills/domainpurchase/src/lib/config.ts +0 -209
  70. package/skills/domainpurchase/src/lib/installer.ts +0 -165
  71. package/skills/managemcp/.env.example +0 -16
  72. package/skills/managemcp/scripts/migrate.ts +0 -57
  73. package/skills/managemcp/scripts/seed.ts +0 -58
  74. package/skills/managemcp/src/db/index.ts +0 -37
  75. package/skills/managemcp/src/lib/api-client.ts +0 -108
  76. package/skills/managemcp/src/lib/config.ts +0 -34
  77. package/skills/managemcp/src/lib/installer.ts +0 -140
  78. package/skills/managemcp/src/lib/service-dir.ts +0 -119
  79. package/skills/sms/scripts/buy-number.ts +0 -63
  80. package/skills/sms/scripts/test-send.ts +0 -67
  81. package/skills/sms/src/server.ts +0 -351
  82. package/skills/sms/src/sse-server.ts +0 -279
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(() => {
@@ -7064,12 +7070,19 @@ import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync
7064
7070
  import { join as join6, dirname as dirname2 } from "path";
7065
7071
  import { homedir as homedir3 } from "os";
7066
7072
  function validKeys() {
7067
- return [...Object.keys(ENUM_KEYS), ...STRING_KEYS];
7073
+ return ["mode", ...Object.keys(ENUM_KEYS), ...STRING_KEYS];
7074
+ }
7075
+ function allowedValues(key) {
7076
+ if (key === "mode")
7077
+ return MODE_VALUES;
7078
+ return ENUM_KEYS[key];
7068
7079
  }
7069
7080
  function normalizeConfigValue(key, value) {
7070
7081
  if (typeof value !== "string")
7071
7082
  return;
7072
- const allowed = ENUM_KEYS[key];
7083
+ if (key === "mode")
7084
+ return MODE_ALIASES[value.trim().toLowerCase()];
7085
+ const allowed = allowedValues(key);
7073
7086
  if (allowed)
7074
7087
  return allowed.includes(value) ? value : undefined;
7075
7088
  if (key === "apiUrl") {
@@ -7127,15 +7140,23 @@ function loadConfig() {
7127
7140
  const projectConfig = readConfigFile(getConfigPath("project"));
7128
7141
  return { ...globalConfig2, ...projectConfig };
7129
7142
  }
7130
- var ENUM_KEYS, STRING_KEYS;
7143
+ var ENUM_KEYS, STRING_KEYS, MODE_VALUES, MODE_ALIASES;
7131
7144
  var init_config = __esm(() => {
7132
7145
  ENUM_KEYS = {
7133
- mode: ["local", "skills.md"],
7134
7146
  defaultAgent: ["claude", "codex", "gemini", "pi", "opencode", "all"],
7135
7147
  defaultScope: ["global", "project"],
7136
7148
  format: ["compact", "json", "csv"]
7137
7149
  };
7138
7150
  STRING_KEYS = ["apiUrl"];
7151
+ MODE_VALUES = ["local", "hosted"];
7152
+ MODE_ALIASES = {
7153
+ local: "local",
7154
+ offline: "local",
7155
+ hosted: "hosted",
7156
+ remote: "hosted",
7157
+ "skills.md": "hosted",
7158
+ skillsmd: "hosted"
7159
+ };
7139
7160
  });
7140
7161
 
7141
7162
  // src/lib/auth-store.ts
@@ -7148,14 +7169,14 @@ __export(exports_auth_store, {
7148
7169
  getApiKey: () => getApiKey,
7149
7170
  clearAuthConfig: () => clearAuthConfig
7150
7171
  });
7151
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4, unlinkSync } from "fs";
7172
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4, unlinkSync } from "fs";
7152
7173
  import { join as join7 } from "path";
7153
7174
  import { homedir as homedir4 } from "os";
7154
7175
  function getAuthConfig() {
7155
7176
  if (cachedConfig !== undefined)
7156
7177
  return cachedConfig;
7157
7178
  try {
7158
- const raw = readFileSync7(AUTH_FILE, "utf-8");
7179
+ const raw = readFileSync7(existsSync7(AUTH_FILE) ? AUTH_FILE : LEGACY_AUTH_FILE, "utf-8");
7159
7180
  const config2 = JSON.parse(raw);
7160
7181
  if (!config2.apiKey || !config2.email) {
7161
7182
  cachedConfig = null;
@@ -7169,7 +7190,7 @@ function getAuthConfig() {
7169
7190
  }
7170
7191
  }
7171
7192
  function saveAuthConfig(config2) {
7172
- mkdirSync4(AUTH_DIR, { recursive: true });
7193
+ mkdirSync4(AUTH_DIR, { recursive: true, mode: 448 });
7173
7194
  writeFileSync4(AUTH_FILE, JSON.stringify(config2, null, 2) + `
7174
7195
  `, { mode: 384 });
7175
7196
  cachedConfig = config2;
@@ -7178,6 +7199,9 @@ function clearAuthConfig() {
7178
7199
  try {
7179
7200
  unlinkSync(AUTH_FILE);
7180
7201
  } catch {}
7202
+ try {
7203
+ unlinkSync(LEGACY_AUTH_FILE);
7204
+ } catch {}
7181
7205
  cachedConfig = null;
7182
7206
  }
7183
7207
  function getApiKey() {
@@ -7200,11 +7224,12 @@ function normalizeSkillsApiOrigin(apiUrl) {
7200
7224
  function getApiUrl() {
7201
7225
  return normalizeSkillsApiOrigin(process.env.SKILLS_API_URL || loadConfig().apiUrl || "https://skills.md");
7202
7226
  }
7203
- var AUTH_DIR, AUTH_FILE, cachedConfig;
7227
+ var AUTH_DIR, AUTH_FILE, LEGACY_AUTH_FILE, cachedConfig;
7204
7228
  var init_auth_store = __esm(() => {
7205
7229
  init_config();
7206
- AUTH_DIR = join7(homedir4(), ".skills");
7230
+ AUTH_DIR = join7(homedir4(), ".hasna", "skills");
7207
7231
  AUTH_FILE = join7(AUTH_DIR, "auth.json");
7232
+ LEGACY_AUTH_FILE = join7(homedir4(), ".skills", "auth.json");
7208
7233
  });
7209
7234
 
7210
7235
  // src/lib/remote-client.ts
@@ -21768,7 +21793,7 @@ class StdioServerTransport {
21768
21793
  // package.json
21769
21794
  var package_default = {
21770
21795
  name: "@hasna/skills",
21771
- version: "0.1.31",
21796
+ version: "0.1.33",
21772
21797
  description: "Skills library for AI coding agents",
21773
21798
  type: "module",
21774
21799
  bin: {
@@ -32882,7 +32907,7 @@ function registerDiscoveryTools(server) {
32882
32907
  }
32883
32908
 
32884
32909
  // src/mcp/operation-tools.ts
32885
- import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
32910
+ import { existsSync as existsSync8, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
32886
32911
  import { join as join8 } from "path";
32887
32912
 
32888
32913
  // src/lib/run-state.ts
@@ -33271,13 +33296,13 @@ function registerOperationTools(server) {
33271
33296
  });
33272
33297
  if (isPremiumSkill2(skillName) && !apiKey) {
33273
33298
  const cost = formatCost2(costCents ?? 0);
33274
- const error48 = `${skillName} is a hosted skill (${cost}). Run: skills setup --mode skills.md && skills auth login`;
33299
+ const error48 = `${skillName} is a hosted skill (${cost}). Run: skills setup --mode hosted && skills auth login`;
33275
33300
  writeRunLogs(runContext, "", error48 + `
33276
33301
  `);
33277
33302
  const run = completeSkillRun(runContext, { status: "failed", error: error48, costCents });
33278
33303
  return mcpError("AUTH_REQUIRED", `${error48}. Local run metadata: ${run.paths.runDir}/run.json`, ["skills auth login"]);
33279
33304
  }
33280
- if (apiKey) {
33305
+ if (isPremiumSkill2(skillName) && apiKey) {
33281
33306
  try {
33282
33307
  const { RemoteSkillsClient: RemoteSkillsClient2 } = await Promise.resolve().then(() => (init_remote_client(), exports_remote_client));
33283
33308
  const client = new RemoteSkillsClient2(apiKey);
@@ -33307,14 +33332,11 @@ function registerOperationTools(server) {
33307
33332
  nextActions: remoteRunNextActions(remoteRunId)
33308
33333
  });
33309
33334
  } catch (err) {
33310
- console.error(`[skills] skills.md API failed for ${skillName}, falling back to local:`, err.message);
33311
- if (isPremiumSkill2(skillName)) {
33312
- const error48 = `Hosted skill ${skillName} requires skills.md access: ${err.message}`;
33313
- writeRunLogs(runContext, "", error48 + `
33335
+ const error48 = `Hosted skill ${skillName} requires hosted access: ${err.message}`;
33336
+ writeRunLogs(runContext, "", error48 + `
33314
33337
  `);
33315
- const localRun2 = completeSkillRun(runContext, { status: "failed", error: error48 });
33316
- return mcpError("PLATFORM_ERROR", `${error48}. Local run metadata: ${localRun2.paths.runDir}/run.json`);
33317
- }
33338
+ const localRun2 = completeSkillRun(runContext, { status: "failed", error: error48 });
33339
+ return mcpError("PLATFORM_ERROR", `${error48}. Local run metadata: ${localRun2.paths.runDir}/run.json`);
33318
33340
  }
33319
33341
  }
33320
33342
  const result = await runSkill(skillName, runArgs, {
@@ -33350,7 +33372,7 @@ function registerOperationTools(server) {
33350
33372
  const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_auth_store(), exports_auth_store));
33351
33373
  const apiKey = getApiKey2();
33352
33374
  if (!apiKey) {
33353
- return mcpError("AUTH_REQUIRED", "Remote run status requires skills.md access. Run: skills auth login", ["skills auth login"]);
33375
+ return mcpError("AUTH_REQUIRED", "Remote run status requires hosted access. Run: skills auth login", ["skills auth login"]);
33354
33376
  }
33355
33377
  const localRun = findSkillRun(run_id);
33356
33378
  const remoteRunId = localRun?.remoteRunId || run_id;
@@ -33436,7 +33458,7 @@ function registerOperationTools(server) {
33436
33458
  const agents = [];
33437
33459
  for (const agent of AGENT_TARGETS) {
33438
33460
  const agentSkillsPath = getAgentSkillsDir(agent, "global");
33439
- const exists = existsSync7(agentSkillsPath);
33461
+ const exists = existsSync8(agentSkillsPath);
33440
33462
  let skillCount = 0;
33441
33463
  if (exists) {
33442
33464
  try {
@@ -33462,7 +33484,7 @@ function registerOperationTools(server) {
33462
33484
  }
33463
33485
 
33464
33486
  // src/lib/feedback.ts
33465
- import { existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
33487
+ import { existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
33466
33488
  import { homedir as homedir5 } from "os";
33467
33489
  import { dirname as dirname3, join as join9 } from "path";
33468
33490
  import { Database } from "bun:sqlite";
@@ -33472,7 +33494,7 @@ function getFeedbackDbPath() {
33472
33494
  function getFeedbackDb() {
33473
33495
  const dbPath = getFeedbackDbPath();
33474
33496
  const dir = dirname3(dbPath);
33475
- if (!existsSync8(dir))
33497
+ if (!existsSync9(dir))
33476
33498
  mkdirSync5(dir, { recursive: true });
33477
33499
  const db = new Database(dbPath);
33478
33500
  db.exec("PRAGMA journal_mode = WAL");
@@ -33621,14 +33643,14 @@ function registerResourceMetaTools(server) {
33621
33643
  }
33622
33644
 
33623
33645
  // src/lib/scheduler.ts
33624
- import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6 } from "fs";
33646
+ import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6 } from "fs";
33625
33647
  import { join as join10 } from "path";
33626
33648
  function getSchedulesPath(targetDir = process.cwd()) {
33627
33649
  return join10(targetDir, ".skills", "schedules.json");
33628
33650
  }
33629
33651
  function loadSchedules(targetDir = process.cwd()) {
33630
33652
  const path = getSchedulesPath(targetDir);
33631
- if (existsSync9(path)) {
33653
+ if (existsSync10(path)) {
33632
33654
  try {
33633
33655
  return JSON.parse(readFileSync8(path, "utf-8"));
33634
33656
  } catch {}
@@ -33638,7 +33660,7 @@ function loadSchedules(targetDir = process.cwd()) {
33638
33660
  function saveSchedules(data, targetDir = process.cwd()) {
33639
33661
  const path = getSchedulesPath(targetDir);
33640
33662
  const dir = join10(targetDir, ".skills");
33641
- if (!existsSync9(dir))
33663
+ if (!existsSync10(dir))
33642
33664
  mkdirSync6(dir, { recursive: true });
33643
33665
  writeFileSync5(path, JSON.stringify(data, null, 2));
33644
33666
  }
@@ -33799,7 +33821,8 @@ function removeSchedule(idOrName, targetDir) {
33799
33821
  }
33800
33822
 
33801
33823
  // src/lib/skill-validation.ts
33802
- import { existsSync as existsSync10, lstatSync, readFileSync as readFileSync9, readdirSync as readdirSync4, statSync as statSync3 } from "fs";
33824
+ init_pricing();
33825
+ import { existsSync as existsSync11, lstatSync, readFileSync as readFileSync9, readdirSync as readdirSync4, statSync as statSync3 } from "fs";
33803
33826
  import { isAbsolute, join as join11, normalize } from "path";
33804
33827
  var DOC_FILES = ["SKILL.md", "README.md", "CLAUDE.md"];
33805
33828
  var RESERVED_SKILL_ENTRIES = new Set([
@@ -33863,6 +33886,25 @@ function isSafeRelativePath(value) {
33863
33886
  const normalized = normalize(value).replace(/\\/g, "/");
33864
33887
  return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
33865
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
+ }
33866
33908
  function parseSkillFrontmatter(content) {
33867
33909
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
33868
33910
  if (!match)
@@ -33912,11 +33954,13 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
33912
33954
  const bareName = name;
33913
33955
  const issues = [];
33914
33956
  const warnings = [];
33957
+ let packageDeclaresHosted = false;
33958
+ let packageSkillSource;
33915
33959
  const metadata = {
33916
33960
  binCommands: [],
33917
33961
  docFiles: []
33918
33962
  };
33919
- if (!existsSync10(skillPath)) {
33963
+ if (!existsSync11(skillPath)) {
33920
33964
  add(issues, "skill.dir_missing", `Skill directory not found: ${skillPath}`);
33921
33965
  return {
33922
33966
  name: bareName,
@@ -33943,14 +33987,14 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
33943
33987
  }
33944
33988
  }
33945
33989
  for (const docFile of DOC_FILES) {
33946
- if (existsSync10(join11(skillPath, docFile)))
33990
+ if (existsSync11(join11(skillPath, docFile)))
33947
33991
  metadata.docFiles.push(docFile);
33948
33992
  }
33949
33993
  if (metadata.docFiles.length === 0) {
33950
33994
  add(issues, "skill.docs_missing", "Missing documentation file: expected SKILL.md, README.md, or CLAUDE.md");
33951
33995
  }
33952
33996
  const skillMdPath = join11(skillPath, "SKILL.md");
33953
- if (existsSync10(skillMdPath)) {
33997
+ if (existsSync11(skillMdPath)) {
33954
33998
  const frontmatter = parseSkillFrontmatter(readFileSync9(skillMdPath, "utf-8"));
33955
33999
  if (!frontmatter) {
33956
34000
  add(warnings, "skill.frontmatter_missing", "SKILL.md has no YAML frontmatter");
@@ -33985,7 +34029,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
33985
34029
  add(warnings, "skill.skill_md_missing", "Missing SKILL.md; registry docs may need generated agent-facing instructions");
33986
34030
  }
33987
34031
  const pkgPath = join11(skillPath, "package.json");
33988
- if (!existsSync10(pkgPath)) {
34032
+ if (!existsSync11(pkgPath)) {
33989
34033
  add(issues, "package.missing", "Missing package.json");
33990
34034
  } else {
33991
34035
  try {
@@ -33994,6 +34038,13 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
33994
34038
  if (!packageRecord) {
33995
34039
  add(issues, "package.invalid_shape", "package.json must be an object");
33996
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";
33997
34048
  if (typeof pkg.name === "string") {
33998
34049
  metadata.packageName = pkg.name;
33999
34050
  if (pkg.name !== bareName) {
@@ -34010,12 +34061,18 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
34010
34061
  ...metadata.provenance ?? { directoryName: bareName },
34011
34062
  ...typeof pkg.name === "string" ? { packageName: pkg.name } : {},
34012
34063
  ...typeof pkg.version === "string" && pkg.version.trim() ? { packageVersion: pkg.version } : {},
34013
- ...registryMeta?.source ? { registrySource: registryMeta.source } : {}
34064
+ ...registryMeta?.source ? { registrySource: registryMeta.source } : {},
34065
+ ...packageSkillSource ? { packageSkillSource } : {}
34014
34066
  };
34015
34067
  const binRecord = asRecord(pkg.bin);
34016
34068
  if (!binRecord || Object.keys(binRecord).length === 0) {
34017
- 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
+ }
34018
34072
  } else {
34073
+ if (hostedMetadata2) {
34074
+ add(issues, "package.hosted_bin_forbidden", "Hosted metadata packages must not expose a local bin entry");
34075
+ }
34019
34076
  for (const [command, target] of Object.entries(binRecord)) {
34020
34077
  if (!VALID_BIN_COMMAND.test(command)) {
34021
34078
  add(issues, "package.bin_command_invalid", `package.json bin command '${command}' must use lowercase letters, numbers, dots, underscores, or hyphens`);
@@ -34030,7 +34087,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
34030
34087
  continue;
34031
34088
  }
34032
34089
  const targetPath = join11(skillPath, target);
34033
- if (!existsSync10(targetPath)) {
34090
+ if (!existsSync11(targetPath)) {
34034
34091
  add(warnings, "package.bin_target_missing", `package.json bin '${command}' target '${target}' is not present before build`);
34035
34092
  } else if (statSync3(targetPath).isDirectory()) {
34036
34093
  add(issues, "package.bin_target_directory", `package.json bin '${command}' target '${target}' must point to a file, not a directory`);
@@ -34042,13 +34099,19 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
34042
34099
  add(issues, "package.invalid_json", `package.json is invalid JSON: ${error48.message}`);
34043
34100
  }
34044
34101
  }
34102
+ const hostedMetadata = isHostedMetadataSkill(bareName, metadata.skillMdFrontmatter, registryMeta, packageDeclaresHosted);
34103
+ metadata.runtime = hostedMetadata ? "hosted" : "local";
34045
34104
  const srcDir = join11(skillPath, "src");
34046
- if (!existsSync10(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)) {
34047
34110
  add(issues, "skill.src_missing", "Missing src/ directory");
34048
- } else if (!existsSync10(join11(srcDir, "index.ts")) && !existsSync10(join11(srcDir, "index.js"))) {
34111
+ } else if (!existsSync11(join11(srcDir, "index.ts")) && !existsSync11(join11(srcDir, "index.js"))) {
34049
34112
  add(issues, "skill.src_index_missing", "Missing src/index.ts or src/index.js");
34050
34113
  } else {
34051
- const indexPath = existsSync10(join11(srcDir, "index.ts")) ? join11(srcDir, "index.ts") : join11(srcDir, "index.js");
34114
+ const indexPath = existsSync11(join11(srcDir, "index.ts")) ? join11(srcDir, "index.ts") : join11(srcDir, "index.js");
34052
34115
  const size = statSync3(indexPath).size;
34053
34116
  if (size < 50)
34054
34117
  add(warnings, "skill.src_index_minimal", `Source entry point is very small (${size}B)`);
@@ -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;
@@ -0,0 +1,13 @@
1
+ import type { Command } from "commander";
2
+ import { type SkillsConfig } from "../lib/config.js";
3
+ export interface FirstRunOnboardingInput {
4
+ argv: string[];
5
+ commandName?: string;
6
+ config: SkillsConfig;
7
+ isInteractive: boolean;
8
+ testMode?: boolean;
9
+ }
10
+ export declare function getRootCommandName(actionCommand: Command): string;
11
+ export declare function shouldShowFirstRunOnboarding(input: FirstRunOnboardingInput): boolean;
12
+ export declare function getFirstRunOnboardingMessage(): string;
13
+ export declare function maybePrintFirstRunOnboarding(actionCommand: Command, argv: string[], isInteractive: boolean): void;