@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/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.31",
1913
+ version: "0.1.33",
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(() => {
@@ -6863,6 +6869,130 @@ var init_discovery = __esm(() => {
6863
6869
  vendorPattern = new RegExp(`\\b(${VENDOR_TERMS.map(escapeRegExp).join("|")})\\b`, "gi");
6864
6870
  });
6865
6871
 
6872
+ // src/lib/config.ts
6873
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, copyFileSync } from "fs";
6874
+ import { join as join4, dirname as dirname2 } from "path";
6875
+ import { homedir as homedir3 } from "os";
6876
+ function validKeys() {
6877
+ return ["mode", ...Object.keys(ENUM_KEYS), ...STRING_KEYS];
6878
+ }
6879
+ function allowedValues(key) {
6880
+ if (key === "mode")
6881
+ return MODE_VALUES;
6882
+ return ENUM_KEYS[key];
6883
+ }
6884
+ function normalizeConfigValue(key, value) {
6885
+ if (typeof value !== "string")
6886
+ return;
6887
+ if (key === "mode")
6888
+ return MODE_ALIASES[value.trim().toLowerCase()];
6889
+ const allowed = allowedValues(key);
6890
+ if (allowed)
6891
+ return allowed.includes(value) ? value : undefined;
6892
+ if (key === "apiUrl") {
6893
+ try {
6894
+ const url = new URL(value);
6895
+ if (url.protocol !== "http:" && url.protocol !== "https:")
6896
+ return;
6897
+ return value.replace(/\/+$/, "");
6898
+ } catch {
6899
+ return;
6900
+ }
6901
+ }
6902
+ return;
6903
+ }
6904
+ function getDataDir() {
6905
+ const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir3();
6906
+ const newDir = join4(home, ".hasna", "skills");
6907
+ const oldConfigFile = join4(home, ".skillsrc");
6908
+ if (existsSync4(oldConfigFile) && !existsSync4(join4(newDir, "config.json"))) {
6909
+ mkdirSync2(newDir, { recursive: true });
6910
+ try {
6911
+ copyFileSync(oldConfigFile, join4(newDir, "config.json"));
6912
+ } catch {}
6913
+ }
6914
+ mkdirSync2(newDir, { recursive: true });
6915
+ return newDir;
6916
+ }
6917
+ function getConfigPath(scope) {
6918
+ if (scope === "global") {
6919
+ return join4(getDataDir(), "config.json");
6920
+ }
6921
+ return join4(process.cwd(), "skills.config.json");
6922
+ }
6923
+ function readConfigFile(path) {
6924
+ if (!existsSync4(path))
6925
+ return {};
6926
+ try {
6927
+ const raw = readFileSync4(path, "utf-8");
6928
+ const parsed = JSON.parse(raw);
6929
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
6930
+ return {};
6931
+ const config = {};
6932
+ for (const key of validKeys()) {
6933
+ const value = normalizeConfigValue(key, parsed[key]);
6934
+ if (value !== undefined)
6935
+ config[key] = value;
6936
+ }
6937
+ return config;
6938
+ } catch {
6939
+ return {};
6940
+ }
6941
+ }
6942
+ function loadConfig() {
6943
+ const globalConfig = readConfigFile(getConfigPath("global"));
6944
+ const projectConfig = readConfigFile(getConfigPath("project"));
6945
+ return { ...globalConfig, ...projectConfig };
6946
+ }
6947
+ function saveConfig(key, value, scope = "project") {
6948
+ if (!validKeys().includes(key)) {
6949
+ throw new Error(`Unknown config key: ${key}. Valid keys: ${validKeys().join(", ")}`);
6950
+ }
6951
+ const normalized = normalizeConfigValue(key, value);
6952
+ if (normalized === undefined) {
6953
+ const allowed = allowedValues(key);
6954
+ throw new Error(allowed ? `Invalid value '${value}' for ${key}. Allowed: ${allowed.join(", ")}` : `Invalid value '${value}' for ${key}. Expected an http(s) URL`);
6955
+ }
6956
+ const filePath = getConfigPath(scope);
6957
+ let existing = {};
6958
+ if (existsSync4(filePath)) {
6959
+ try {
6960
+ existing = JSON.parse(readFileSync4(filePath, "utf-8"));
6961
+ if (typeof existing !== "object" || existing === null || Array.isArray(existing)) {
6962
+ existing = {};
6963
+ }
6964
+ } catch {
6965
+ existing = {};
6966
+ }
6967
+ } else {
6968
+ const dir = dirname2(filePath);
6969
+ if (!existsSync4(dir)) {
6970
+ mkdirSync2(dir, { recursive: true });
6971
+ }
6972
+ }
6973
+ existing[key] = normalized;
6974
+ writeFileSync2(filePath, JSON.stringify(existing, null, 2) + `
6975
+ `);
6976
+ }
6977
+ var ENUM_KEYS, STRING_KEYS, MODE_VALUES, MODE_ALIASES;
6978
+ var init_config = __esm(() => {
6979
+ ENUM_KEYS = {
6980
+ defaultAgent: ["claude", "codex", "gemini", "pi", "opencode", "all"],
6981
+ defaultScope: ["global", "project"],
6982
+ format: ["compact", "json", "csv"]
6983
+ };
6984
+ STRING_KEYS = ["apiUrl"];
6985
+ MODE_VALUES = ["local", "hosted"];
6986
+ MODE_ALIASES = {
6987
+ local: "local",
6988
+ offline: "local",
6989
+ hosted: "hosted",
6990
+ remote: "hosted",
6991
+ "skills.md": "hosted",
6992
+ skillsmd: "hosted"
6993
+ };
6994
+ });
6995
+
6866
6996
  // node_modules/zod/v4/core/core.js
6867
6997
  function $constructor(name, initializer, params) {
6868
6998
  function init(inst, def) {
@@ -20729,115 +20859,6 @@ var init_zod = __esm(() => {
20729
20859
  init_external();
20730
20860
  });
20731
20861
 
20732
- // src/lib/config.ts
20733
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, copyFileSync } from "fs";
20734
- import { join as join4, dirname as dirname2 } from "path";
20735
- import { homedir as homedir3 } from "os";
20736
- function validKeys() {
20737
- return [...Object.keys(ENUM_KEYS), ...STRING_KEYS];
20738
- }
20739
- function normalizeConfigValue(key, value) {
20740
- if (typeof value !== "string")
20741
- return;
20742
- const allowed = ENUM_KEYS[key];
20743
- if (allowed)
20744
- return allowed.includes(value) ? value : undefined;
20745
- if (key === "apiUrl") {
20746
- try {
20747
- const url2 = new URL(value);
20748
- if (url2.protocol !== "http:" && url2.protocol !== "https:")
20749
- return;
20750
- return value.replace(/\/+$/, "");
20751
- } catch {
20752
- return;
20753
- }
20754
- }
20755
- return;
20756
- }
20757
- function getDataDir() {
20758
- const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir3();
20759
- const newDir = join4(home, ".hasna", "skills");
20760
- const oldConfigFile = join4(home, ".skillsrc");
20761
- if (existsSync4(oldConfigFile) && !existsSync4(join4(newDir, "config.json"))) {
20762
- mkdirSync2(newDir, { recursive: true });
20763
- try {
20764
- copyFileSync(oldConfigFile, join4(newDir, "config.json"));
20765
- } catch {}
20766
- }
20767
- mkdirSync2(newDir, { recursive: true });
20768
- return newDir;
20769
- }
20770
- function getConfigPath(scope) {
20771
- if (scope === "global") {
20772
- return join4(getDataDir(), "config.json");
20773
- }
20774
- return join4(process.cwd(), "skills.config.json");
20775
- }
20776
- function readConfigFile(path) {
20777
- if (!existsSync4(path))
20778
- return {};
20779
- try {
20780
- const raw = readFileSync4(path, "utf-8");
20781
- const parsed = JSON.parse(raw);
20782
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
20783
- return {};
20784
- const config2 = {};
20785
- for (const key of validKeys()) {
20786
- const value = normalizeConfigValue(key, parsed[key]);
20787
- if (value !== undefined)
20788
- config2[key] = value;
20789
- }
20790
- return config2;
20791
- } catch {
20792
- return {};
20793
- }
20794
- }
20795
- function loadConfig() {
20796
- const globalConfig2 = readConfigFile(getConfigPath("global"));
20797
- const projectConfig = readConfigFile(getConfigPath("project"));
20798
- return { ...globalConfig2, ...projectConfig };
20799
- }
20800
- function saveConfig(key, value, scope = "project") {
20801
- if (!validKeys().includes(key)) {
20802
- throw new Error(`Unknown config key: ${key}. Valid keys: ${validKeys().join(", ")}`);
20803
- }
20804
- const normalized = normalizeConfigValue(key, value);
20805
- if (normalized === undefined) {
20806
- const allowed = ENUM_KEYS[key];
20807
- throw new Error(allowed ? `Invalid value '${value}' for ${key}. Allowed: ${allowed.join(", ")}` : `Invalid value '${value}' for ${key}. Expected an http(s) URL`);
20808
- }
20809
- const filePath = getConfigPath(scope);
20810
- let existing = {};
20811
- if (existsSync4(filePath)) {
20812
- try {
20813
- existing = JSON.parse(readFileSync4(filePath, "utf-8"));
20814
- if (typeof existing !== "object" || existing === null || Array.isArray(existing)) {
20815
- existing = {};
20816
- }
20817
- } catch {
20818
- existing = {};
20819
- }
20820
- } else {
20821
- const dir = dirname2(filePath);
20822
- if (!existsSync4(dir)) {
20823
- mkdirSync2(dir, { recursive: true });
20824
- }
20825
- }
20826
- existing[key] = normalized;
20827
- writeFileSync2(filePath, JSON.stringify(existing, null, 2) + `
20828
- `);
20829
- }
20830
- var ENUM_KEYS, STRING_KEYS;
20831
- var init_config = __esm(() => {
20832
- ENUM_KEYS = {
20833
- mode: ["local", "skills.md"],
20834
- defaultAgent: ["claude", "codex", "gemini", "pi", "opencode", "all"],
20835
- defaultScope: ["global", "project"],
20836
- format: ["compact", "json", "csv"]
20837
- };
20838
- STRING_KEYS = ["apiUrl"];
20839
- });
20840
-
20841
20862
  // src/lib/auth-store.ts
20842
20863
  var exports_auth_store = {};
20843
20864
  __export(exports_auth_store, {
@@ -20848,14 +20869,14 @@ __export(exports_auth_store, {
20848
20869
  getApiKey: () => getApiKey,
20849
20870
  clearAuthConfig: () => clearAuthConfig
20850
20871
  });
20851
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, unlinkSync } from "fs";
20872
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, unlinkSync } from "fs";
20852
20873
  import { join as join5 } from "path";
20853
20874
  import { homedir as homedir4 } from "os";
20854
20875
  function getAuthConfig() {
20855
20876
  if (cachedConfig !== undefined)
20856
20877
  return cachedConfig;
20857
20878
  try {
20858
- const raw = readFileSync5(AUTH_FILE, "utf-8");
20879
+ const raw = readFileSync5(existsSync5(AUTH_FILE) ? AUTH_FILE : LEGACY_AUTH_FILE, "utf-8");
20859
20880
  const config2 = JSON.parse(raw);
20860
20881
  if (!config2.apiKey || !config2.email) {
20861
20882
  cachedConfig = null;
@@ -20869,7 +20890,7 @@ function getAuthConfig() {
20869
20890
  }
20870
20891
  }
20871
20892
  function saveAuthConfig(config2) {
20872
- mkdirSync3(AUTH_DIR, { recursive: true });
20893
+ mkdirSync3(AUTH_DIR, { recursive: true, mode: 448 });
20873
20894
  writeFileSync3(AUTH_FILE, JSON.stringify(config2, null, 2) + `
20874
20895
  `, { mode: 384 });
20875
20896
  cachedConfig = config2;
@@ -20878,6 +20899,9 @@ function clearAuthConfig() {
20878
20899
  try {
20879
20900
  unlinkSync(AUTH_FILE);
20880
20901
  } catch {}
20902
+ try {
20903
+ unlinkSync(LEGACY_AUTH_FILE);
20904
+ } catch {}
20881
20905
  cachedConfig = null;
20882
20906
  }
20883
20907
  function getApiKey() {
@@ -20900,11 +20924,12 @@ function normalizeSkillsApiOrigin(apiUrl) {
20900
20924
  function getApiUrl() {
20901
20925
  return normalizeSkillsApiOrigin(process.env.SKILLS_API_URL || loadConfig().apiUrl || "https://skills.md");
20902
20926
  }
20903
- var AUTH_DIR, AUTH_FILE, cachedConfig;
20927
+ var AUTH_DIR, AUTH_FILE, LEGACY_AUTH_FILE, cachedConfig;
20904
20928
  var init_auth_store = __esm(() => {
20905
20929
  init_config();
20906
- AUTH_DIR = join5(homedir4(), ".skills");
20930
+ AUTH_DIR = join5(homedir4(), ".hasna", "skills");
20907
20931
  AUTH_FILE = join5(AUTH_DIR, "auth.json");
20932
+ LEGACY_AUTH_FILE = join5(homedir4(), ".skills", "auth.json");
20908
20933
  });
20909
20934
 
20910
20935
  // src/lib/remote-registry.ts
@@ -21543,11 +21568,11 @@ __export(exports_skillinfo, {
21543
21568
  generateEnvExample: () => generateEnvExample,
21544
21569
  detectProjectSkills: () => detectProjectSkills
21545
21570
  });
21546
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
21571
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
21547
21572
  import { join as join6 } from "path";
21548
21573
  function getSkillDocs(name) {
21549
21574
  const skillPath = getSkillPath(name);
21550
- if (!existsSync5(skillPath))
21575
+ if (!existsSync6(skillPath))
21551
21576
  return null;
21552
21577
  return {
21553
21578
  skillMd: readIfExists(join6(skillPath, "SKILL.md")),
@@ -21563,7 +21588,7 @@ function getSkillBestDoc(name) {
21563
21588
  }
21564
21589
  function getSkillRequirements(name) {
21565
21590
  const skillPath = getSkillPath(name);
21566
- if (!existsSync5(skillPath))
21591
+ if (!existsSync6(skillPath))
21567
21592
  return null;
21568
21593
  const texts = [];
21569
21594
  for (const file2 of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
@@ -21606,7 +21631,7 @@ function getSkillRequirements(name) {
21606
21631
  let cliCommand = `skills run ${skillName}`;
21607
21632
  let dependencies = {};
21608
21633
  const pkgPath = join6(skillPath, "package.json");
21609
- if (existsSync5(pkgPath)) {
21634
+ if (existsSync6(pkgPath)) {
21610
21635
  try {
21611
21636
  const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
21612
21637
  dependencies = pkg.dependencies || {};
@@ -21625,11 +21650,11 @@ function isHostedPremiumSkill(skillName, meta3) {
21625
21650
  async function runSkill(name, args, options = {}) {
21626
21651
  const canonicalName = getSkill(name)?.name ?? name;
21627
21652
  const skillPath = getSkillPath(canonicalName);
21628
- if (!existsSync5(skillPath)) {
21653
+ if (!existsSync6(skillPath)) {
21629
21654
  return { exitCode: 1, error: `Skill '${name}' not found` };
21630
21655
  }
21631
21656
  const pkgPath = join6(skillPath, "package.json");
21632
- if (!existsSync5(pkgPath)) {
21657
+ if (!existsSync6(pkgPath)) {
21633
21658
  return { exitCode: 1, error: `No package.json in skill '${name}'` };
21634
21659
  }
21635
21660
  let entryPoint;
@@ -21649,11 +21674,11 @@ async function runSkill(name, args, options = {}) {
21649
21674
  return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
21650
21675
  }
21651
21676
  const entryPath = join6(skillPath, entryPoint);
21652
- if (!existsSync5(entryPath)) {
21677
+ if (!existsSync6(entryPath)) {
21653
21678
  return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
21654
21679
  }
21655
21680
  const nodeModules = join6(skillPath, "node_modules");
21656
- if (!existsSync5(nodeModules)) {
21681
+ if (!existsSync6(nodeModules)) {
21657
21682
  const install = Bun.spawn(["bun", "install", "--no-save"], {
21658
21683
  cwd: skillPath,
21659
21684
  stdout: "pipe",
@@ -21681,7 +21706,7 @@ async function runSkill(name, args, options = {}) {
21681
21706
  }
21682
21707
  function detectProjectSkills(cwd = process.cwd()) {
21683
21708
  const pkgPath = join6(cwd, "package.json");
21684
- if (!existsSync5(pkgPath)) {
21709
+ if (!existsSync6(pkgPath)) {
21685
21710
  const alwaysRecommend = ["implementation-plan", "write", "deepresearch"];
21686
21711
  const recommended2 = alwaysRecommend.map((name) => loadRegistry().find((s) => s.name === name)).filter((s) => s !== undefined);
21687
21712
  return { detected: [], recommended: recommended2 };
@@ -21812,7 +21837,7 @@ function generateSkillMd(name) {
21812
21837
  if (!meta3)
21813
21838
  return null;
21814
21839
  const skillPath = getSkillPath(name);
21815
- if (!existsSync5(skillPath))
21840
+ if (!existsSync6(skillPath))
21816
21841
  return null;
21817
21842
  const frontmatter = [
21818
21843
  "---",
@@ -21825,7 +21850,7 @@ function generateSkillMd(name) {
21825
21850
  const claudeMd = readIfExists(join6(skillPath, "CLAUDE.md"));
21826
21851
  let cliCommand = null;
21827
21852
  const pkgPath = join6(skillPath, "package.json");
21828
- if (existsSync5(pkgPath)) {
21853
+ if (existsSync6(pkgPath)) {
21829
21854
  try {
21830
21855
  const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
21831
21856
  if (pkg.bin) {
@@ -21900,7 +21925,7 @@ function extractEnvVars(text) {
21900
21925
  }
21901
21926
  function readIfExists(path) {
21902
21927
  try {
21903
- if (existsSync5(path)) {
21928
+ if (existsSync6(path)) {
21904
21929
  return readFileSync6(path, "utf-8");
21905
21930
  }
21906
21931
  } catch {}
@@ -21932,7 +21957,7 @@ var init_skillinfo = __esm(() => {
21932
21957
  });
21933
21958
 
21934
21959
  // src/lib/skill-validation.ts
21935
- import { existsSync as existsSync6, lstatSync, readFileSync as readFileSync7, readdirSync as readdirSync2, statSync } from "fs";
21960
+ import { existsSync as existsSync7, lstatSync, readFileSync as readFileSync7, readdirSync as readdirSync2, statSync } from "fs";
21936
21961
  import { isAbsolute, join as join7, normalize } from "path";
21937
21962
  function add(target, code, message) {
21938
21963
  target.push({ code, message });
@@ -21952,6 +21977,25 @@ function isSafeRelativePath(value) {
21952
21977
  const normalized = normalize(value).replace(/\\/g, "/");
21953
21978
  return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
21954
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
+ }
21955
21999
  function parseSkillFrontmatter(content) {
21956
22000
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
21957
22001
  if (!match)
@@ -22001,11 +22045,13 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22001
22045
  const bareName = name;
22002
22046
  const issues = [];
22003
22047
  const warnings = [];
22048
+ let packageDeclaresHosted = false;
22049
+ let packageSkillSource;
22004
22050
  const metadata = {
22005
22051
  binCommands: [],
22006
22052
  docFiles: []
22007
22053
  };
22008
- if (!existsSync6(skillPath)) {
22054
+ if (!existsSync7(skillPath)) {
22009
22055
  add(issues, "skill.dir_missing", `Skill directory not found: ${skillPath}`);
22010
22056
  return {
22011
22057
  name: bareName,
@@ -22032,14 +22078,14 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22032
22078
  }
22033
22079
  }
22034
22080
  for (const docFile of DOC_FILES) {
22035
- if (existsSync6(join7(skillPath, docFile)))
22081
+ if (existsSync7(join7(skillPath, docFile)))
22036
22082
  metadata.docFiles.push(docFile);
22037
22083
  }
22038
22084
  if (metadata.docFiles.length === 0) {
22039
22085
  add(issues, "skill.docs_missing", "Missing documentation file: expected SKILL.md, README.md, or CLAUDE.md");
22040
22086
  }
22041
22087
  const skillMdPath = join7(skillPath, "SKILL.md");
22042
- if (existsSync6(skillMdPath)) {
22088
+ if (existsSync7(skillMdPath)) {
22043
22089
  const frontmatter = parseSkillFrontmatter(readFileSync7(skillMdPath, "utf-8"));
22044
22090
  if (!frontmatter) {
22045
22091
  add(warnings, "skill.frontmatter_missing", "SKILL.md has no YAML frontmatter");
@@ -22074,7 +22120,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22074
22120
  add(warnings, "skill.skill_md_missing", "Missing SKILL.md; registry docs may need generated agent-facing instructions");
22075
22121
  }
22076
22122
  const pkgPath = join7(skillPath, "package.json");
22077
- if (!existsSync6(pkgPath)) {
22123
+ if (!existsSync7(pkgPath)) {
22078
22124
  add(issues, "package.missing", "Missing package.json");
22079
22125
  } else {
22080
22126
  try {
@@ -22083,6 +22129,13 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22083
22129
  if (!packageRecord) {
22084
22130
  add(issues, "package.invalid_shape", "package.json must be an object");
22085
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";
22086
22139
  if (typeof pkg.name === "string") {
22087
22140
  metadata.packageName = pkg.name;
22088
22141
  if (pkg.name !== bareName) {
@@ -22099,12 +22152,18 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22099
22152
  ...metadata.provenance ?? { directoryName: bareName },
22100
22153
  ...typeof pkg.name === "string" ? { packageName: pkg.name } : {},
22101
22154
  ...typeof pkg.version === "string" && pkg.version.trim() ? { packageVersion: pkg.version } : {},
22102
- ...registryMeta?.source ? { registrySource: registryMeta.source } : {}
22155
+ ...registryMeta?.source ? { registrySource: registryMeta.source } : {},
22156
+ ...packageSkillSource ? { packageSkillSource } : {}
22103
22157
  };
22104
22158
  const binRecord = asRecord(pkg.bin);
22105
22159
  if (!binRecord || Object.keys(binRecord).length === 0) {
22106
- 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
+ }
22107
22163
  } else {
22164
+ if (hostedMetadata2) {
22165
+ add(issues, "package.hosted_bin_forbidden", "Hosted metadata packages must not expose a local bin entry");
22166
+ }
22108
22167
  for (const [command, target] of Object.entries(binRecord)) {
22109
22168
  if (!VALID_BIN_COMMAND.test(command)) {
22110
22169
  add(issues, "package.bin_command_invalid", `package.json bin command '${command}' must use lowercase letters, numbers, dots, underscores, or hyphens`);
@@ -22119,7 +22178,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22119
22178
  continue;
22120
22179
  }
22121
22180
  const targetPath = join7(skillPath, target);
22122
- if (!existsSync6(targetPath)) {
22181
+ if (!existsSync7(targetPath)) {
22123
22182
  add(warnings, "package.bin_target_missing", `package.json bin '${command}' target '${target}' is not present before build`);
22124
22183
  } else if (statSync(targetPath).isDirectory()) {
22125
22184
  add(issues, "package.bin_target_directory", `package.json bin '${command}' target '${target}' must point to a file, not a directory`);
@@ -22131,13 +22190,19 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22131
22190
  add(issues, "package.invalid_json", `package.json is invalid JSON: ${error48.message}`);
22132
22191
  }
22133
22192
  }
22193
+ const hostedMetadata = isHostedMetadataSkill(bareName, metadata.skillMdFrontmatter, registryMeta, packageDeclaresHosted);
22194
+ metadata.runtime = hostedMetadata ? "hosted" : "local";
22134
22195
  const srcDir = join7(skillPath, "src");
22135
- if (!existsSync6(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)) {
22136
22201
  add(issues, "skill.src_missing", "Missing src/ directory");
22137
- } else if (!existsSync6(join7(srcDir, "index.ts")) && !existsSync6(join7(srcDir, "index.js"))) {
22202
+ } else if (!existsSync7(join7(srcDir, "index.ts")) && !existsSync7(join7(srcDir, "index.js"))) {
22138
22203
  add(issues, "skill.src_index_missing", "Missing src/index.ts or src/index.js");
22139
22204
  } else {
22140
- const indexPath = existsSync6(join7(srcDir, "index.ts")) ? join7(srcDir, "index.ts") : join7(srcDir, "index.js");
22205
+ const indexPath = existsSync7(join7(srcDir, "index.ts")) ? join7(srcDir, "index.ts") : join7(srcDir, "index.js");
22141
22206
  const size = statSync(indexPath).size;
22142
22207
  if (size < 50)
22143
22208
  add(warnings, "skill.src_index_minimal", `Source entry point is very small (${size}B)`);
@@ -22153,6 +22218,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
22153
22218
  }
22154
22219
  var DOC_FILES, RESERVED_SKILL_ENTRIES, KNOWN_TOP_LEVEL_ENTRIES, VALID_PROVENANCE_SOURCES, VALID_BIN_COMMAND;
22155
22220
  var init_skill_validation = __esm(() => {
22221
+ init_pricing();
22156
22222
  DOC_FILES = ["SKILL.md", "README.md", "CLAUDE.md"];
22157
22223
  RESERVED_SKILL_ENTRIES = new Set([
22158
22224
  ".env",
@@ -22205,7 +22271,7 @@ __export(exports_introspect, {
22205
22271
  registerIntrospect: () => registerIntrospect
22206
22272
  });
22207
22273
  import chalk4 from "chalk";
22208
- import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
22274
+ import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
22209
22275
  import { join as join8 } from "path";
22210
22276
  import { execSync } from "child_process";
22211
22277
  function registerIntrospect(parent) {
@@ -22417,7 +22483,7 @@ function handleValidate(name, options) {
22417
22483
  function handleDiff(name, options) {
22418
22484
  const bare = name;
22419
22485
  const sourcePath = getSkillPath(bare);
22420
- if (!existsSync7(sourcePath)) {
22486
+ if (!existsSync8(sourcePath)) {
22421
22487
  if (options.json)
22422
22488
  console.log(JSON.stringify({ error: `Skill '${bare}' not found in registry` }));
22423
22489
  else
@@ -22436,7 +22502,7 @@ function handleDiff(name, options) {
22436
22502
  const installedVersion = installMeta.skills[bare]?.version ?? "unknown";
22437
22503
  const registryPkgPath = join8(sourcePath, "package.json");
22438
22504
  let registryVersion = "unknown";
22439
- if (existsSync7(registryPkgPath)) {
22505
+ if (existsSync8(registryPkgPath)) {
22440
22506
  try {
22441
22507
  registryVersion = JSON.parse(readFileSync8(registryPkgPath, "utf-8")).version || "unknown";
22442
22508
  } catch {}
@@ -22469,7 +22535,7 @@ __export(exports_init, {
22469
22535
  registerSetup: () => registerSetup
22470
22536
  });
22471
22537
  import chalk5 from "chalk";
22472
- import { existsSync as existsSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync4, appendFileSync } from "fs";
22538
+ import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync4, appendFileSync } from "fs";
22473
22539
  import { join as join9 } from "path";
22474
22540
  function registerSetup(parent) {
22475
22541
  parent.command("init").option("--json", "Output as JSON", false).option("--for <agent>", "Detect project type and show MCP registration guidance for agent").option("--scope <scope>", "Deprecated; agent skill-folder installs are disabled", "global").description("Initialize project for pinned skills (.env.example, .gitignore)").action((options) => handleInit(options));
@@ -22564,7 +22630,7 @@ Use: skills mcp --register ${options.for}`));
22564
22630
  console.log(chalk5.dim(" No environment variables detected across pinned skills"));
22565
22631
  const gitignorePath = join9(cwd, ".gitignore");
22566
22632
  const gitignoreEntries = [".skills/runs/", ".skills/exports/", ".skills/tmp/"];
22567
- let gitignoreContent = existsSync8(gitignorePath) ? readFileSync9(gitignorePath, "utf-8") : "";
22633
+ let gitignoreContent = existsSync9(gitignorePath) ? readFileSync9(gitignorePath, "utf-8") : "";
22568
22634
  let gitignoreUpdated = false;
22569
22635
  const missingEntries = gitignoreEntries.filter((entry) => !gitignoreContent.includes(entry));
22570
22636
  if (missingEntries.length > 0) {
@@ -22610,7 +22676,7 @@ async function handleImport(file2, options) {
22610
22676
  if (file2 === "-")
22611
22677
  raw = await new Response(process.stdin).text();
22612
22678
  else {
22613
- if (!existsSync8(file2)) {
22679
+ if (!existsSync9(file2)) {
22614
22680
  const error48 = `File not found: ${file2}`;
22615
22681
  if (options.json)
22616
22682
  console.log(JSON.stringify({ imported: 0, error: error48 }));
@@ -22720,7 +22786,7 @@ __export(exports_diagnostic, {
22720
22786
  registerDiagnostic: () => registerDiagnostic
22721
22787
  });
22722
22788
  import chalk6 from "chalk";
22723
- import { existsSync as existsSync9, readFileSync as readFileSync10, readdirSync as readdirSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
22789
+ import { existsSync as existsSync10, readFileSync as readFileSync10, readdirSync as readdirSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
22724
22790
  import { join as join10 } from "path";
22725
22791
  import { execSync as execSync2 } from "child_process";
22726
22792
  function registerDiagnostic(parent) {
@@ -22856,7 +22922,7 @@ function handleAuth(name, options) {
22856
22922
  process.exitCode = 1;
22857
22923
  return;
22858
22924
  }
22859
- let existing = existsSync9(envFilePath) ? readFileSync10(envFilePath, "utf-8") : "";
22925
+ let existing = existsSync10(envFilePath) ? readFileSync10(envFilePath, "utf-8") : "";
22860
22926
  const keyPattern = new RegExp(`^${key}=.*$`, "m");
22861
22927
  const updated = keyPattern.test(existing) ? existing.replace(keyPattern, `${key}=${value}`) : existing.endsWith(`
22862
22928
  `) || existing === "" ? existing + `${key}=${value}
@@ -22922,7 +22988,7 @@ function handleWhoami(options) {
22922
22988
  const agentConfigs = [];
22923
22989
  for (const agent of AGENT_TARGETS) {
22924
22990
  const agentSkillsPath = getAgentSkillsDir(agent, "global");
22925
- const exists = existsSync9(agentSkillsPath);
22991
+ const exists = existsSync10(agentSkillsPath);
22926
22992
  let skillCount = 0;
22927
22993
  if (exists)
22928
22994
  try {
@@ -22967,7 +23033,7 @@ function handleOutdated(options) {
22967
23033
  const registryPath = getSkillPath(name);
22968
23034
  const registryPkgPath = join10(registryPath, "package.json");
22969
23035
  let registryVersion = "unknown";
22970
- if (existsSync9(registryPkgPath))
23036
+ if (existsSync10(registryPkgPath))
22971
23037
  try {
22972
23038
  registryVersion = JSON.parse(readFileSync10(registryPkgPath, "utf-8")).version || "unknown";
22973
23039
  } catch {}
@@ -23058,7 +23124,7 @@ var REMOTE_SKILL_RUN_CONTRACT_VERSION = 1;
23058
23124
 
23059
23125
  // src/lib/run-state.ts
23060
23126
  import { createHash, randomBytes } from "crypto";
23061
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync11, readdirSync as readdirSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
23127
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync11, readdirSync as readdirSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
23062
23128
  import { extname, join as join11, relative } from "path";
23063
23129
  function createSkillRun(params, targetDir = process.cwd()) {
23064
23130
  const now = new Date;
@@ -23125,12 +23191,12 @@ function appendRunEvent(context, event, data = {}) {
23125
23191
  const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
23126
23192
  `;
23127
23193
  const path = join11(context.runDir, "events.ndjson");
23128
- const previous = existsSync10(path) ? readFileSync11(path, "utf-8") : "";
23194
+ const previous = existsSync11(path) ? readFileSync11(path, "utf-8") : "";
23129
23195
  writeFileSync6(path, previous + line);
23130
23196
  }
23131
23197
  function listSkillRuns(targetDir = process.cwd(), limit = 50) {
23132
23198
  const runsRoot = join11(getProjectStateDir(targetDir), "runs");
23133
- if (!existsSync10(runsRoot))
23199
+ if (!existsSync11(runsRoot))
23134
23200
  return [];
23135
23201
  const records = [];
23136
23202
  for (const day of readdirSync4(runsRoot).sort().reverse()) {
@@ -23149,7 +23215,7 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
23149
23215
  }
23150
23216
  function findSkillRun(runId, targetDir = process.cwd()) {
23151
23217
  const runsRoot = join11(getProjectStateDir(targetDir), "runs");
23152
- if (!existsSync10(runsRoot))
23218
+ if (!existsSync11(runsRoot))
23153
23219
  return null;
23154
23220
  for (const day of readdirSync4(runsRoot)) {
23155
23221
  const record2 = readRunRecord(join11(runsRoot, day, runId));
@@ -23170,7 +23236,7 @@ function writeArtifactsManifest(context, artifacts) {
23170
23236
  `);
23171
23237
  }
23172
23238
  function collectRunArtifacts(context) {
23173
- if (!existsSync10(context.exportDir))
23239
+ if (!existsSync11(context.exportDir))
23174
23240
  return [];
23175
23241
  const artifacts = [];
23176
23242
  for (const path of walkFiles(context.exportDir)) {
@@ -23187,7 +23253,7 @@ function collectRunArtifacts(context) {
23187
23253
  }
23188
23254
  function readRunRecord(runDir) {
23189
23255
  const path = join11(runDir, "run.json");
23190
- if (!existsSync10(path))
23256
+ if (!existsSync11(path))
23191
23257
  return null;
23192
23258
  try {
23193
23259
  return JSON.parse(readFileSync11(path, "utf-8"));
@@ -39276,7 +39342,7 @@ var init_remote_client = __esm(() => {
39276
39342
  });
39277
39343
 
39278
39344
  // src/mcp/operation-tools.ts
39279
- import { existsSync as existsSync11, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
39345
+ import { existsSync as existsSync12, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
39280
39346
  import { join as join12 } from "path";
39281
39347
  function registerOperationTools(server) {
39282
39348
  server.registerTool("pin_skill", {
@@ -39497,13 +39563,13 @@ function registerOperationTools(server) {
39497
39563
  });
39498
39564
  if (isPremiumSkill2(skillName) && !apiKey) {
39499
39565
  const cost = formatCost2(costCents ?? 0);
39500
- const error48 = `${skillName} is a hosted skill (${cost}). Run: skills setup --mode skills.md && skills auth login`;
39566
+ const error48 = `${skillName} is a hosted skill (${cost}). Run: skills setup --mode hosted && skills auth login`;
39501
39567
  writeRunLogs(runContext, "", error48 + `
39502
39568
  `);
39503
39569
  const run = completeSkillRun(runContext, { status: "failed", error: error48, costCents });
39504
39570
  return mcpError("AUTH_REQUIRED", `${error48}. Local run metadata: ${run.paths.runDir}/run.json`, ["skills auth login"]);
39505
39571
  }
39506
- if (apiKey) {
39572
+ if (isPremiumSkill2(skillName) && apiKey) {
39507
39573
  try {
39508
39574
  const { RemoteSkillsClient: RemoteSkillsClient2 } = await Promise.resolve().then(() => (init_remote_client(), exports_remote_client));
39509
39575
  const client = new RemoteSkillsClient2(apiKey);
@@ -39533,14 +39599,11 @@ function registerOperationTools(server) {
39533
39599
  nextActions: remoteRunNextActions(remoteRunId)
39534
39600
  });
39535
39601
  } catch (err) {
39536
- console.error(`[skills] skills.md API failed for ${skillName}, falling back to local:`, err.message);
39537
- if (isPremiumSkill2(skillName)) {
39538
- const error48 = `Hosted skill ${skillName} requires skills.md access: ${err.message}`;
39539
- writeRunLogs(runContext, "", error48 + `
39602
+ const error48 = `Hosted skill ${skillName} requires hosted access: ${err.message}`;
39603
+ writeRunLogs(runContext, "", error48 + `
39540
39604
  `);
39541
- const localRun2 = completeSkillRun(runContext, { status: "failed", error: error48 });
39542
- return mcpError("PLATFORM_ERROR", `${error48}. Local run metadata: ${localRun2.paths.runDir}/run.json`);
39543
- }
39605
+ const localRun2 = completeSkillRun(runContext, { status: "failed", error: error48 });
39606
+ return mcpError("PLATFORM_ERROR", `${error48}. Local run metadata: ${localRun2.paths.runDir}/run.json`);
39544
39607
  }
39545
39608
  }
39546
39609
  const result = await runSkill(skillName, runArgs, {
@@ -39576,7 +39639,7 @@ function registerOperationTools(server) {
39576
39639
  const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_auth_store(), exports_auth_store));
39577
39640
  const apiKey = getApiKey2();
39578
39641
  if (!apiKey) {
39579
- return mcpError("AUTH_REQUIRED", "Remote run status requires skills.md access. Run: skills auth login", ["skills auth login"]);
39642
+ return mcpError("AUTH_REQUIRED", "Remote run status requires hosted access. Run: skills auth login", ["skills auth login"]);
39580
39643
  }
39581
39644
  const localRun = findSkillRun(run_id);
39582
39645
  const remoteRunId = localRun?.remoteRunId || run_id;
@@ -39662,7 +39725,7 @@ function registerOperationTools(server) {
39662
39725
  const agents = [];
39663
39726
  for (const agent of AGENT_TARGETS) {
39664
39727
  const agentSkillsPath = getAgentSkillsDir(agent, "global");
39665
- const exists = existsSync11(agentSkillsPath);
39728
+ const exists = existsSync12(agentSkillsPath);
39666
39729
  let skillCount = 0;
39667
39730
  if (exists) {
39668
39731
  try {
@@ -39697,7 +39760,7 @@ var init_operation_tools = __esm(() => {
39697
39760
  });
39698
39761
 
39699
39762
  // src/lib/feedback.ts
39700
- import { existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
39763
+ import { existsSync as existsSync13, mkdirSync as mkdirSync5 } from "fs";
39701
39764
  import { homedir as homedir5 } from "os";
39702
39765
  import { dirname as dirname3, join as join13 } from "path";
39703
39766
  import { Database } from "bun:sqlite";
@@ -39707,7 +39770,7 @@ function getFeedbackDbPath() {
39707
39770
  function getFeedbackDb() {
39708
39771
  const dbPath = getFeedbackDbPath();
39709
39772
  const dir = dirname3(dbPath);
39710
- if (!existsSync12(dir))
39773
+ if (!existsSync13(dir))
39711
39774
  mkdirSync5(dir, { recursive: true });
39712
39775
  const db = new Database(dbPath);
39713
39776
  db.exec("PRAGMA journal_mode = WAL");
@@ -39868,14 +39931,14 @@ var init_resource_meta_tools = __esm(() => {
39868
39931
  });
39869
39932
 
39870
39933
  // src/lib/scheduler.ts
39871
- import { existsSync as existsSync13, readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6 } from "fs";
39934
+ import { existsSync as existsSync14, readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6 } from "fs";
39872
39935
  import { join as join14 } from "path";
39873
39936
  function getSchedulesPath(targetDir = process.cwd()) {
39874
39937
  return join14(targetDir, ".skills", "schedules.json");
39875
39938
  }
39876
39939
  function loadSchedules(targetDir = process.cwd()) {
39877
39940
  const path = getSchedulesPath(targetDir);
39878
- if (existsSync13(path)) {
39941
+ if (existsSync14(path)) {
39879
39942
  try {
39880
39943
  return JSON.parse(readFileSync12(path, "utf-8"));
39881
39944
  } catch {}
@@ -39885,7 +39948,7 @@ function loadSchedules(targetDir = process.cwd()) {
39885
39948
  function saveSchedules(data, targetDir = process.cwd()) {
39886
39949
  const path = getSchedulesPath(targetDir);
39887
39950
  const dir = join14(targetDir, ".skills");
39888
- if (!existsSync13(dir))
39951
+ if (!existsSync14(dir))
39889
39952
  mkdirSync6(dir, { recursive: true });
39890
39953
  writeFileSync7(path, JSON.stringify(data, null, 2));
39891
39954
  }
@@ -40430,7 +40493,7 @@ var init_mcp2 = __esm(() => {
40430
40493
 
40431
40494
  // src/cli/commands/runtime-mcp.ts
40432
40495
  import chalk7 from "chalk";
40433
- import { existsSync as existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "fs";
40496
+ import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "fs";
40434
40497
  import { homedir as homedir6 } from "os";
40435
40498
  import { dirname as dirname4, join as join15 } from "path";
40436
40499
  async function handleMcp(options) {
@@ -40540,7 +40603,7 @@ function registerCodexMcp(command) {
40540
40603
  const config2 = `[mcp_servers.${MCP_SERVER_NAME}]
40541
40604
  command = ${JSON.stringify(command)}`;
40542
40605
  try {
40543
- const current = existsSync14(path) ? readFileSync13(path, "utf-8") : "";
40606
+ const current = existsSync15(path) ? readFileSync13(path, "utf-8") : "";
40544
40607
  writeTextFile(path, upsertTomlSection(current, `[mcp_servers.${MCP_SERVER_NAME}]`, `command = ${JSON.stringify(command)}`));
40545
40608
  return { agent: "codex", success: true, path, config: config2 };
40546
40609
  } catch (err) {
@@ -40590,7 +40653,7 @@ function registerJsonMcpServer(agent, path, containerKey, server2) {
40590
40653
  }
40591
40654
  }
40592
40655
  function readJsonObject(path) {
40593
- if (!existsSync14(path))
40656
+ if (!existsSync15(path))
40594
40657
  return {};
40595
40658
  const raw = readFileSync13(path, "utf-8").trim();
40596
40659
  if (!raw)
@@ -40637,7 +40700,7 @@ function findCommandOnPath(command) {
40637
40700
  if (!dir)
40638
40701
  continue;
40639
40702
  const candidate = join15(dir, command);
40640
- if (existsSync14(candidate))
40703
+ if (existsSync15(candidate))
40641
40704
  return candidate;
40642
40705
  }
40643
40706
  return command;
@@ -40670,7 +40733,7 @@ function registerRuntime(parent) {
40670
40733
  exportsCommand.command("open").argument("<run-id>", "Run id").option("--json", "Output as JSON", false).description("Open the export directory for a run").action((runId, options) => handleExportsOpen(runId, options));
40671
40734
  exportsCommand.command("download").argument("<run-id>", "Remote run id").option("--json", "Output as JSON", false).description("Download remote run artifacts into .skills/exports").action((runId, options) => handleExportsDownload(runId, options));
40672
40735
  parent.command("mcp").option("--register <agent>", "Register MCP server with agent").option("--json", "Output registration result as JSON", false).description("Start MCP server (stdio) or register with an agent").action(async (options) => handleMcp(options));
40673
- const setup = parent.command("setup").description("Choose local-only mode, skills.md mode, or agent integrations").option("--mode <mode>", "Runtime mode: local or skills.md").option("--api-url <url>", "skills.md-compatible API origin for hosted mode").option("--global", "Save setup choice globally", false).option("--json", "Output setup result as JSON", false).action(async (options) => handleSetup(options));
40736
+ const setup = parent.command("setup").description("Choose hosted mode, local-only mode, or agent integrations").option("--mode <mode>", "Runtime mode: hosted or local").option("--api-url <url>", "Hosted API origin").option("--global", "Save setup choice globally", false).option("--json", "Output setup result as JSON", false).action(async (options) => handleSetup(options));
40674
40737
  setup.command("agents").option("--json", "Output registration result as JSON", false).description("Register the Skills MCP server with all supported agents").action(async (options) => handleMcp({ register: "all", json: options.json }));
40675
40738
  parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).action(async (options) => {
40676
40739
  if (process.env.SKILLS_TEST_MODE === "1") {
@@ -40719,15 +40782,15 @@ async function handleSetup(options) {
40719
40782
  const scope = options.global ? "global" : "project";
40720
40783
  let mode = normalizeSetupMode(options.mode);
40721
40784
  if (!mode && process.stdin.isTTY && process.stdout.isTTY) {
40722
- mode = normalizeSetupMode(await promptLine("Use Skills locally or with skills.md? [local/skills.md] "));
40785
+ mode = normalizeSetupMode(await promptLine("Use hosted Skills or local-only mode? [hosted/local] ")) ?? "hosted";
40723
40786
  }
40724
40787
  mode = mode ?? "local";
40725
40788
  saveConfig("mode", mode, scope);
40726
- if (mode === "skills.md") {
40789
+ if (mode === "hosted") {
40727
40790
  saveConfig("apiUrl", options.apiUrl || "https://skills.md", scope);
40728
40791
  }
40729
40792
  const config2 = loadConfig();
40730
- const next = mode === "skills.md" ? ["skills auth login", "skills list --remote"] : ["skills list", "skills run <skill>"];
40793
+ const next = mode === "hosted" ? ["skills auth login", "skills list --remote"] : ["skills list", "skills run <skill>"];
40731
40794
  const payload = {
40732
40795
  mode,
40733
40796
  scope,
@@ -40740,7 +40803,7 @@ async function handleSetup(options) {
40740
40803
  }
40741
40804
  console.log(chalk8.green(`Set Skills mode to ${mode}`));
40742
40805
  console.log(chalk8.dim(` Scope: ${scope}`));
40743
- if (mode === "skills.md") {
40806
+ if (mode === "hosted") {
40744
40807
  console.log(chalk8.dim(` API: ${config2.apiUrl || "https://skills.md"}`));
40745
40808
  console.log(chalk8.dim(" Next: skills auth login"));
40746
40809
  } else {
@@ -40755,8 +40818,8 @@ function normalizeSetupMode(value) {
40755
40818
  if (normalized === "local" || normalized === "offline")
40756
40819
  return "local";
40757
40820
  if (normalized === "skills.md" || normalized === "skillsmd" || normalized === "remote" || normalized === "hosted")
40758
- return "skills.md";
40759
- throw new Error("Invalid setup mode. Use local or skills.md.");
40821
+ return "hosted";
40822
+ throw new Error("Invalid setup mode. Use hosted or local.");
40760
40823
  }
40761
40824
  function promptLine(question) {
40762
40825
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -40845,7 +40908,7 @@ async function handleRun(name, args2, options) {
40845
40908
  const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_auth_store(), exports_auth_store));
40846
40909
  const apiKey = getApiKey2();
40847
40910
  if (!apiKey) {
40848
- const error48 = `${skill.name} is a hosted skill (${pricing.formatCost(costCents ?? 0)}). Run: skills setup --mode skills.md && skills auth login`;
40911
+ const error48 = `${skill.name} is a hosted skill (${pricing.formatCost(costCents ?? 0)}). Run: skills setup --mode hosted && skills auth login`;
40849
40912
  writeRunLogs(runContext, "", error48 + `
40850
40913
  `);
40851
40914
  const run = completeSkillRun(runContext, { status: "failed", error: error48, costCents });
@@ -40927,7 +40990,7 @@ async function handleRun(name, args2, options) {
40927
40990
  process.exitCode = exitCode;
40928
40991
  return;
40929
40992
  } catch (err) {
40930
- const error48 = `Hosted skill ${skill.name} requires skills.md access: ${err.message}`;
40993
+ const error48 = `Hosted skill ${skill.name} requires hosted access: ${err.message}`;
40931
40994
  writeRunLogs(runContext, "", error48 + `
40932
40995
  `);
40933
40996
  const run = completeSkillRun(runContext, { status: "failed", error: error48, costCents });
@@ -41033,7 +41096,7 @@ async function handleRunsStatus(runId, options) {
41033
41096
  const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_auth_store(), exports_auth_store));
41034
41097
  const apiKey = getApiKey2();
41035
41098
  if (!apiKey) {
41036
- const error48 = "Remote run status requires skills.md access. Run: skills auth login";
41099
+ const error48 = "Remote run status requires hosted access. Run: skills auth login";
41037
41100
  if (options.json)
41038
41101
  console.log(JSON.stringify({ contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION, error: error48 }, null, 2));
41039
41102
  else
@@ -41134,7 +41197,7 @@ async function handleExportsDownload(runId, options) {
41134
41197
  const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_auth_store(), exports_auth_store));
41135
41198
  const apiKey = getApiKey2();
41136
41199
  if (!apiKey) {
41137
- const error48 = "Remote artifact downloads require skills.md access. Run: skills auth login";
41200
+ const error48 = "Remote artifact downloads require hosted access. Run: skills auth login";
41138
41201
  if (options.json)
41139
41202
  console.log(JSON.stringify({ error: error48 }, null, 2));
41140
41203
  else
@@ -41530,13 +41593,14 @@ var init_completion = __esm(() => {
41530
41593
  "categories",
41531
41594
  "tags",
41532
41595
  "mcp",
41533
- "serve",
41534
41596
  "self-update",
41535
41597
  "completion",
41536
41598
  "export",
41537
41599
  "import",
41538
41600
  "doctor",
41539
41601
  "auth",
41602
+ "billing",
41603
+ "credits",
41540
41604
  "env-check",
41541
41605
  "setup-info",
41542
41606
  "test",
@@ -41562,7 +41626,7 @@ __export(exports_create_sync_config, {
41562
41626
  registerCreateSync: () => registerCreateSync
41563
41627
  });
41564
41628
  import chalk9 from "chalk";
41565
- import { existsSync as existsSync15, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9 } from "fs";
41629
+ import { existsSync as existsSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9 } from "fs";
41566
41630
  import { join as join17 } from "path";
41567
41631
  import { homedir as homedir7 } from "os";
41568
41632
  function registerCreateSync(parent) {
@@ -41612,13 +41676,13 @@ function registerCreateSync(parent) {
41612
41676
  const pp = getConfigPath("project");
41613
41677
  if (options.json) {
41614
41678
  console.log(JSON.stringify({
41615
- global: { path: gp, exists: existsSync15(gp) },
41616
- project: { path: pp, exists: existsSync15(pp) }
41679
+ global: { path: gp, exists: existsSync16(gp) },
41680
+ project: { path: pp, exists: existsSync16(pp) }
41617
41681
  }, null, 2));
41618
41682
  return;
41619
41683
  }
41620
- console.log(`${chalk9.cyan("global")}: ${gp}${existsSync15(gp) ? chalk9.green(" (exists)") : chalk9.dim(" (not found)")}`);
41621
- console.log(`${chalk9.cyan("project")}: ${pp}${existsSync15(pp) ? chalk9.green(" (exists)") : chalk9.dim(" (not found)")}`);
41684
+ console.log(`${chalk9.cyan("global")}: ${gp}${existsSync16(gp) ? chalk9.green(" (exists)") : chalk9.dim(" (not found)")}`);
41685
+ console.log(`${chalk9.cyan("project")}: ${pp}${existsSync16(pp) ? chalk9.green(" (exists)") : chalk9.dim(" (not found)")}`);
41622
41686
  });
41623
41687
  parent.command("create").argument("<name>", "Skill name (e.g. my-tool)").option("--category <category>", "Skill category", "Development Tools").option("--description <description>", "Short description of what the skill does").option("--tags <tags>", "Comma-separated tags (e.g. api,testing,automation)").option("--global", "Deprecated; custom skills are always global", false).option("--json", "Output result as JSON", false).description("Scaffold a new custom skill directory").action((name, options) => handleCreate(name, options));
41624
41688
  parent.command("sync").option("--to <agent>", "Deprecated; use skills mcp --register <agent|all>").option("--from <agent>", "Deprecated; agent skill-folder sync is disabled").option("--register", "Deprecated; agent skill-folder imports are disabled", false).option("--scope <scope>", "Deprecated; ignored", "global").option("--json", "Output as JSON", false).description("Disabled legacy agent skill-folder sync").action((options) => handleSync(options));
@@ -41628,7 +41692,7 @@ function handleCreate(name, options) {
41628
41692
  const dirName = bare;
41629
41693
  const baseDir = join17(homedir7(), ".hasna", "skills", "custom");
41630
41694
  const skillDir = join17(baseDir, dirName);
41631
- if (existsSync15(skillDir)) {
41695
+ if (existsSync16(skillDir)) {
41632
41696
  console.log(options.json ? JSON.stringify({ error: `Skill '${bare}' already exists at ${skillDir}` }) : chalk9.red(`Skill '${bare}' already exists at ${skillDir}`));
41633
41697
  process.exitCode = 1;
41634
41698
  return;
@@ -41840,7 +41904,7 @@ async function executeScheduledSkill(skillName, args2) {
41840
41904
  const { getApiKey: getApiKey2 } = await Promise.resolve().then(() => (init_auth_store(), exports_auth_store));
41841
41905
  const apiKey = getApiKey2();
41842
41906
  if (!apiKey) {
41843
- throw new Error(`${skill.name} is a hosted skill. Run: skills setup --mode skills.md && skills auth login`);
41907
+ throw new Error(`${skill.name} is a hosted skill. Run: skills setup --mode hosted && skills auth login`);
41844
41908
  }
41845
41909
  const { RemoteSkillsClient: RemoteSkillsClient2 } = await Promise.resolve().then(() => (init_remote_client(), exports_remote_client));
41846
41910
  const client = new RemoteSkillsClient2(apiKey);
@@ -42004,6 +42068,66 @@ async function apiRequest(path, options) {
42004
42068
  });
42005
42069
  return res.json();
42006
42070
  }
42071
+ function sleep(ms) {
42072
+ return new Promise((resolve) => setTimeout(resolve, ms));
42073
+ }
42074
+ function browserCommand(url2) {
42075
+ if (process.platform === "darwin")
42076
+ return ["open", url2];
42077
+ if (process.platform === "win32")
42078
+ return ["cmd", "/c", "start", "", url2];
42079
+ return ["xdg-open", url2];
42080
+ }
42081
+ function openBrowser(url2) {
42082
+ const command = browserCommand(url2);
42083
+ if (!command)
42084
+ return;
42085
+ try {
42086
+ Bun.spawn(command, { stdout: "ignore", stderr: "ignore" });
42087
+ } catch {}
42088
+ }
42089
+ async function ensureApiKey(loginResult) {
42090
+ if (loginResult.apiKey)
42091
+ return loginResult.apiKey;
42092
+ if (!loginResult.token)
42093
+ return;
42094
+ const keyRes = await apiRequest("/api/auth/keys", {
42095
+ method: "POST",
42096
+ headers: { Authorization: `Bearer ${loginResult.token}` },
42097
+ body: JSON.stringify({ name: "cli" })
42098
+ });
42099
+ return keyRes.key;
42100
+ }
42101
+ async function persistLoginResult(loginResult) {
42102
+ const storedKey = await ensureApiKey(loginResult);
42103
+ if (!storedKey)
42104
+ return;
42105
+ saveAuthConfig({
42106
+ apiKey: storedKey,
42107
+ email: loginResult.user.email,
42108
+ orgId: loginResult.organization.id,
42109
+ orgSlug: loginResult.organization.slug,
42110
+ userId: loginResult.user.id
42111
+ });
42112
+ return storedKey;
42113
+ }
42114
+ function printLoginSuccess(loginResult, json2) {
42115
+ if (json2 || !isTTY) {
42116
+ console.log(JSON.stringify({
42117
+ status: "authenticated",
42118
+ email: loginResult.user.email,
42119
+ organization: loginResult.organization.slug,
42120
+ firstLogin: loginResult.firstLogin
42121
+ }));
42122
+ return;
42123
+ }
42124
+ console.log(chalk12.green(`
42125
+ \u2713 Signed in as ${loginResult.user.email}`));
42126
+ console.log(chalk12.dim(` Organization: ${loginResult.organization.name}`));
42127
+ if (loginResult.firstLogin) {
42128
+ console.log(chalk12.dim(` API key saved to ~/.hasna/skills/auth.json`));
42129
+ }
42130
+ }
42007
42131
  async function doLogin(email3, code) {
42008
42132
  if (!email3 || !email3.includes("@")) {
42009
42133
  console.error(chalk12.red("Invalid email"));
@@ -42037,41 +42161,99 @@ async function doLogin(email3, code) {
42037
42161
  process.exitCode = 1;
42038
42162
  return;
42039
42163
  }
42040
- let storedKey = verifyRes.apiKey;
42164
+ const storedKey = await persistLoginResult(verifyRes);
42041
42165
  if (!storedKey) {
42042
- const keyRes = await apiRequest("/api/auth/keys", {
42166
+ console.error(chalk12.red("Login succeeded but API key creation failed"));
42167
+ process.exitCode = 1;
42168
+ return;
42169
+ }
42170
+ printLoginSuccess(verifyRes, false);
42171
+ }
42172
+ async function doDeviceLogin(options) {
42173
+ const start = await apiRequest("/api/auth/device/start", {
42174
+ method: "POST",
42175
+ body: JSON.stringify({ client: "skills-cli" })
42176
+ });
42177
+ if (start.error) {
42178
+ console.error(chalk12.red(start.error));
42179
+ process.exitCode = 1;
42180
+ return;
42181
+ }
42182
+ const verificationUrl = start.verificationUriComplete || start.verificationUri;
42183
+ const shouldPoll = Boolean(options.poll || isTTY && !options.json);
42184
+ if (options.open !== false && isTTY && verificationUrl) {
42185
+ openBrowser(verificationUrl);
42186
+ }
42187
+ if (!shouldPoll) {
42188
+ const payload = {
42189
+ status: "pending",
42190
+ userCode: start.userCode,
42191
+ verificationUri: start.verificationUri,
42192
+ verificationUriComplete: start.verificationUriComplete,
42193
+ expiresIn: start.expiresIn,
42194
+ interval: start.interval,
42195
+ poll: "skills auth login --device --poll"
42196
+ };
42197
+ if (options.json || !isTTY)
42198
+ console.log(JSON.stringify(payload, null, 2));
42199
+ else {
42200
+ console.log(chalk12.bold(`
42201
+ Sign in in your browser
42202
+ `));
42203
+ console.log(`${chalk12.dim("Code:")} ${start.userCode}`);
42204
+ console.log(`${chalk12.dim("URL:")} ${verificationUrl}`);
42205
+ }
42206
+ return;
42207
+ }
42208
+ if (!options.json) {
42209
+ console.log(chalk12.bold(`
42210
+ Sign in in your browser
42211
+ `));
42212
+ console.log(`${chalk12.dim("Code:")} ${start.userCode}`);
42213
+ console.log(`${chalk12.dim("URL:")} ${verificationUrl}`);
42214
+ console.log(chalk12.dim(`
42215
+ Waiting for authentication...`));
42216
+ }
42217
+ const intervalMs = Math.max(1000, Number(start.interval || 5) * 1000);
42218
+ const timeoutMs = Number(options.pollTimeoutMs || DEFAULT_DEVICE_POLL_TIMEOUT_MS);
42219
+ const deadline = Date.now() + timeoutMs;
42220
+ while (Date.now() < deadline) {
42221
+ const tokenRes = await apiRequest("/api/auth/device/token", {
42043
42222
  method: "POST",
42044
- headers: { Authorization: `Bearer ${verifyRes.token}` },
42045
- body: JSON.stringify({ name: "cli" })
42223
+ body: JSON.stringify({ deviceCode: start.deviceCode })
42046
42224
  });
42047
- storedKey = keyRes.key;
42048
- }
42049
- saveAuthConfig({
42050
- apiKey: storedKey,
42051
- email: verifyRes.user.email,
42052
- orgId: verifyRes.organization.id,
42053
- orgSlug: verifyRes.organization.slug,
42054
- userId: verifyRes.user.id
42055
- });
42056
- if (isTTY) {
42057
- console.log(chalk12.green(`
42058
- \u2713 Signed in as ${verifyRes.user.email}`));
42059
- console.log(chalk12.dim(` Organization: ${verifyRes.organization.name}`));
42060
- if (verifyRes.firstLogin) {
42061
- console.log(chalk12.dim(` API key saved to ~/.skills/auth.json`));
42225
+ if (tokenRes.error === "authorization_pending" || tokenRes.status === "pending") {
42226
+ await sleep(intervalMs);
42227
+ continue;
42062
42228
  }
42063
- } else {
42064
- console.log(JSON.stringify({
42065
- status: "authenticated",
42066
- email: verifyRes.user.email,
42067
- organization: verifyRes.organization.slug,
42068
- firstLogin: verifyRes.firstLogin
42069
- }));
42229
+ if (tokenRes.error) {
42230
+ console.error(chalk12.red(tokenRes.detail || tokenRes.error));
42231
+ process.exitCode = 1;
42232
+ return;
42233
+ }
42234
+ const storedKey = await persistLoginResult(tokenRes);
42235
+ if (!storedKey) {
42236
+ console.error(chalk12.red("Login succeeded but API key creation failed"));
42237
+ process.exitCode = 1;
42238
+ return;
42239
+ }
42240
+ printLoginSuccess(tokenRes, Boolean(options.json));
42241
+ return;
42070
42242
  }
42243
+ const error48 = "Device login timed out before browser authentication completed";
42244
+ if (options.json || !isTTY)
42245
+ console.log(JSON.stringify({ status: "expired", error: error48 }));
42246
+ else
42247
+ console.error(chalk12.red(error48));
42248
+ process.exitCode = 1;
42071
42249
  }
42072
42250
  function registerAuth(parent) {
42073
- const auth = parent.command("auth").description("Manage your skills.md account");
42074
- auth.command("login").description("Sign in with your email (passwordless)").option("--email <email>", "Email address (non-interactive)").option("--code <code>", "Verification code (non-interactive)").action(async (options) => {
42251
+ const auth = parent.command("auth").description("Manage hosted account authentication");
42252
+ auth.command("login").description("Sign in with browser/device code or email code").option("--email <email>", "Email address (non-interactive)").option("--code <code>", "Verification code (non-interactive)").option("--device", "Use browser/device-code login", false).option("--no-open", "Do not open a browser for device-code login").option("--poll", "Poll until browser authentication completes in non-interactive mode", false).option("--poll-timeout-ms <ms>", "Maximum time to wait for device-code login").option("--json", "Output result as JSON", false).action(async (options) => {
42253
+ if (options.device || !options.email && !options.code) {
42254
+ await doDeviceLogin(options);
42255
+ return;
42256
+ }
42075
42257
  let email3 = options.email;
42076
42258
  if (!email3 && isTTY) {
42077
42259
  const existing = getAuthConfig();
@@ -42140,79 +42322,137 @@ function registerAuth(parent) {
42140
42322
  console.log(chalk12.dim("(offline \u2014 showing cached info)"));
42141
42323
  }
42142
42324
  });
42143
- auth.command("status").description("Show billing status and credits").action(async () => {
42144
- const config2 = getAuthConfig();
42145
- if (!config2) {
42146
- console.log(chalk12.dim("Not signed in. Run: skills auth login"));
42147
- return;
42148
- }
42149
- try {
42150
- const res = await apiRequest("/api/v1/billing/status", {
42151
- headers: { Authorization: `Bearer ${config2.apiKey}` }
42152
- });
42153
- console.log(chalk12.bold("Plan: ") + res.plan);
42154
- console.log(chalk12.bold("Balance: ") + res.balance);
42155
- } catch {
42156
- console.error(chalk12.red("Failed to fetch billing status"));
42157
- }
42158
- });
42159
- auth.command("checkout").description("Create a Pro checkout session").action(async () => {
42160
- const config2 = getAuthConfig();
42161
- if (!config2) {
42162
- console.log(chalk12.dim("Not signed in. Run: skills auth login"));
42163
- return;
42164
- }
42165
- const res = await apiRequest("/api/v1/billing/checkout", {
42166
- method: "POST",
42325
+ auth.command("status").description("Show hosted billing status").option("--json", "Output as JSON", false).action(handleBillingStatus);
42326
+ auth.command("checkout").description("Create a Pro checkout session").option("--json", "Output as JSON", false).action(handleCheckout);
42327
+ auth.command("portal").description("Create a customer portal session").option("--json", "Output as JSON", false).action(handlePortal);
42328
+ auth.command("buy-credits").description("Create a credit pack checkout session").argument("<amount>", "Credit pack amount: 1, 5, 20, 50, or 100").option("--json", "Output as JSON", false).action(handleBuyCredits);
42329
+ registerBilling(parent);
42330
+ registerCredits(parent);
42331
+ }
42332
+ function requireHostedAuth(json2) {
42333
+ const config2 = getAuthConfig();
42334
+ if (config2)
42335
+ return config2;
42336
+ const message = "Not signed in. Run: skills auth login";
42337
+ if (json2)
42338
+ console.log(JSON.stringify({ error: message }));
42339
+ else
42340
+ console.log(chalk12.dim(message));
42341
+ process.exitCode = 1;
42342
+ return null;
42343
+ }
42344
+ async function handleBillingStatus(options = {}) {
42345
+ const config2 = requireHostedAuth(options.json);
42346
+ if (!config2)
42347
+ return;
42348
+ try {
42349
+ const res = await apiRequest("/api/v1/billing/status", {
42167
42350
  headers: { Authorization: `Bearer ${config2.apiKey}` }
42168
42351
  });
42169
- if (res.error || !res.url) {
42170
- console.error(chalk12.red(res.detail || res.error || "Failed to create checkout session"));
42171
- process.exitCode = 1;
42352
+ if (options.json) {
42353
+ console.log(JSON.stringify(res, null, 2));
42172
42354
  return;
42173
42355
  }
42356
+ console.log(chalk12.bold("Plan: ") + res.plan);
42357
+ console.log(chalk12.bold("Balance: ") + res.balance);
42358
+ } catch {
42359
+ console.error(chalk12.red("Failed to fetch billing status"));
42360
+ process.exitCode = 1;
42361
+ }
42362
+ }
42363
+ async function handleCheckout(options = {}) {
42364
+ const config2 = requireHostedAuth(options.json);
42365
+ if (!config2)
42366
+ return;
42367
+ const res = await apiRequest("/api/v1/billing/checkout", {
42368
+ method: "POST",
42369
+ headers: { Authorization: `Bearer ${config2.apiKey}` }
42370
+ });
42371
+ if (res.error || !res.url) {
42372
+ console.error(chalk12.red(res.detail || res.error || "Failed to create checkout session"));
42373
+ process.exitCode = 1;
42374
+ return;
42375
+ }
42376
+ if (options.json)
42377
+ console.log(JSON.stringify(res, null, 2));
42378
+ else
42174
42379
  console.log(res.url);
42380
+ }
42381
+ async function handlePortal(options = {}) {
42382
+ const config2 = requireHostedAuth(options.json);
42383
+ if (!config2)
42384
+ return;
42385
+ const res = await apiRequest("/api/v1/billing/portal", {
42386
+ method: "POST",
42387
+ headers: { Authorization: `Bearer ${config2.apiKey}` }
42175
42388
  });
42176
- auth.command("portal").description("Create a customer portal session").action(async () => {
42177
- const config2 = getAuthConfig();
42178
- if (!config2) {
42179
- console.log(chalk12.dim("Not signed in. Run: skills auth login"));
42180
- return;
42181
- }
42182
- const res = await apiRequest("/api/v1/billing/portal", {
42183
- method: "POST",
42184
- headers: { Authorization: `Bearer ${config2.apiKey}` }
42185
- });
42186
- if (res.error || !res.url) {
42187
- console.error(chalk12.red(res.detail || res.error || "Failed to create customer portal session"));
42188
- process.exitCode = 1;
42189
- return;
42190
- }
42389
+ if (res.error || !res.url) {
42390
+ console.error(chalk12.red(res.detail || res.error || "Failed to create customer portal session"));
42391
+ process.exitCode = 1;
42392
+ return;
42393
+ }
42394
+ if (options.json)
42395
+ console.log(JSON.stringify(res, null, 2));
42396
+ else
42191
42397
  console.log(res.url);
42398
+ }
42399
+ async function handleBuyCredits(amount, options = {}) {
42400
+ const config2 = requireHostedAuth(options.json);
42401
+ if (!config2)
42402
+ return;
42403
+ const res = await apiRequest("/api/v1/billing/credits", {
42404
+ method: "POST",
42405
+ headers: { Authorization: `Bearer ${config2.apiKey}` },
42406
+ body: JSON.stringify({ amount })
42192
42407
  });
42193
- auth.command("buy-credits").description("Create a credit pack checkout session").argument("<amount>", "Credit pack amount: 1, 5, 20, 50, or 100").action(async (amount) => {
42194
- const config2 = getAuthConfig();
42195
- if (!config2) {
42196
- console.log(chalk12.dim("Not signed in. Run: skills auth login"));
42197
- return;
42198
- }
42199
- const res = await apiRequest("/api/v1/billing/credits", {
42200
- method: "POST",
42201
- headers: { Authorization: `Bearer ${config2.apiKey}` },
42202
- body: JSON.stringify({ amount })
42203
- });
42204
- if (res.error || !res.url) {
42205
- console.error(chalk12.red(res.detail || res.error || "Failed to create credit checkout session"));
42206
- process.exitCode = 1;
42207
- return;
42208
- }
42408
+ if (res.error || !res.url) {
42409
+ console.error(chalk12.red(res.detail || res.error || "Failed to create credit checkout session"));
42410
+ process.exitCode = 1;
42411
+ return;
42412
+ }
42413
+ if (options.json)
42414
+ console.log(JSON.stringify(res, null, 2));
42415
+ else
42209
42416
  console.log(res.url);
42417
+ }
42418
+ async function handleListCreditPacks(options = {}) {
42419
+ const config2 = requireHostedAuth(options.json);
42420
+ if (!config2)
42421
+ return;
42422
+ const res = await apiRequest("/api/v1/billing/credits", {
42423
+ headers: { Authorization: `Bearer ${config2.apiKey}` }
42210
42424
  });
42425
+ if (res.error) {
42426
+ console.error(chalk12.red(res.detail || res.error || "Failed to list credit packs"));
42427
+ process.exitCode = 1;
42428
+ return;
42429
+ }
42430
+ if (options.json) {
42431
+ console.log(JSON.stringify(res, null, 2));
42432
+ return;
42433
+ }
42434
+ const packs = Array.isArray(res) ? res : res.packs;
42435
+ for (const pack of packs ?? []) {
42436
+ console.log(`${pack.amount}: ${pack.amountCents ? `$${(pack.amountCents / 100).toFixed(2)}` : pack.label || ""}`);
42437
+ }
42211
42438
  }
42212
- var isTTY;
42439
+ function registerBilling(parent) {
42440
+ const billing = parent.command("billing").description("Manage hosted billing");
42441
+ billing.command("status").description("Show billing status").option("--json", "Output as JSON", false).action(handleBillingStatus);
42442
+ billing.command("checkout").description("Create a Pro checkout session").option("--json", "Output as JSON", false).action(handleCheckout);
42443
+ billing.command("portal").description("Create a customer portal session").option("--json", "Output as JSON", false).action(handlePortal);
42444
+ billing.command("buy-credits").description("Create a credit pack checkout session").argument("<amount>", "Credit pack amount: 1, 5, 20, 50, or 100").option("--json", "Output as JSON", false).action(handleBuyCredits);
42445
+ }
42446
+ function registerCredits(parent) {
42447
+ const credits = parent.command("credits").description("Manage hosted credit packs");
42448
+ credits.command("buy").description("Create a credit pack checkout session").argument("<amount>", "Credit pack amount: 1, 5, 20, 50, or 100").option("--json", "Output as JSON", false).action(handleBuyCredits);
42449
+ credits.command("packs").description("List available credit packs").option("--json", "Output as JSON", false).action(handleListCreditPacks);
42450
+ }
42451
+ var isTTY, DEFAULT_DEVICE_POLL_TIMEOUT_MS;
42213
42452
  var init_auth = __esm(() => {
42214
42453
  init_auth_store();
42215
42454
  isTTY = process.stdin.isTTY && process.stdout.isTTY;
42455
+ DEFAULT_DEVICE_POLL_TIMEOUT_MS = 10 * 60 * 1000;
42216
42456
  });
42217
42457
 
42218
42458
  // src/cli/commands/feedback.ts
@@ -43382,6 +43622,58 @@ function App({ initialSkills, overwrite = false }) {
43382
43622
  // src/cli/index.tsx
43383
43623
  init_registry();
43384
43624
  init_discovery();
43625
+
43626
+ // src/cli/onboarding.ts
43627
+ init_config();
43628
+ var SKIP_ONBOARDING_COMMANDS = new Set([
43629
+ "interactive",
43630
+ "setup",
43631
+ "config",
43632
+ "auth",
43633
+ "billing",
43634
+ "credits",
43635
+ "completion",
43636
+ "self-update"
43637
+ ]);
43638
+ function getRootCommandName(actionCommand) {
43639
+ let current = actionCommand;
43640
+ while (current.parent && current.parent.parent)
43641
+ current = current.parent;
43642
+ return current.name();
43643
+ }
43644
+ function shouldShowFirstRunOnboarding(input) {
43645
+ if (!input.isInteractive || input.testMode)
43646
+ return false;
43647
+ if (input.config.mode)
43648
+ return false;
43649
+ if (input.argv.some((arg) => arg === "--json" || arg === "--help" || arg === "-h" || arg === "--version" || arg === "-V")) {
43650
+ return false;
43651
+ }
43652
+ if (!input.commandName || SKIP_ONBOARDING_COMMANDS.has(input.commandName))
43653
+ return false;
43654
+ return input.argv.length > 0;
43655
+ }
43656
+ function getFirstRunOnboardingMessage() {
43657
+ return [
43658
+ "No Skills setup found.",
43659
+ " Hosted: skills setup --mode hosted && skills auth login",
43660
+ " Local: skills setup --mode local"
43661
+ ].join(`
43662
+ `);
43663
+ }
43664
+ function maybePrintFirstRunOnboarding(actionCommand, argv, isInteractive) {
43665
+ if (shouldShowFirstRunOnboarding({
43666
+ argv,
43667
+ commandName: getRootCommandName(actionCommand),
43668
+ config: loadConfig(),
43669
+ isInteractive,
43670
+ testMode: process.env.SKILLS_TEST_MODE === "1"
43671
+ })) {
43672
+ console.error(getFirstRunOnboardingMessage());
43673
+ }
43674
+ }
43675
+
43676
+ // src/cli/index.tsx
43385
43677
  import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
43386
43678
  var isTTY2 = (process.stdout.isTTY ?? false) && (process.stdin.isTTY ?? false);
43387
43679
  if (process.argv.includes("--no-color")) {
@@ -43422,4 +43714,7 @@ var { registerAuth: registerAuth2 } = await Promise.resolve().then(() => (init_a
43422
43714
  registerAuth2(program2);
43423
43715
  var { registerFeedback: registerFeedback2 } = await Promise.resolve().then(() => (init_feedback2(), exports_feedback));
43424
43716
  registerFeedback2(program2);
43717
+ program2.hook("preAction", (_thisCommand, actionCommand) => {
43718
+ maybePrintFirstRunOnboarding(actionCommand, process.argv.slice(2), isTTY2);
43719
+ });
43425
43720
  await program2.parseAsync();