@safeskill/cli 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Changelog
2
2
 
3
- ## 0.3.0
3
+ ## 0.3.2
4
+
5
+ - Fixed the published npm binary entrypoint so `npx @safeskill/cli` executes the bundled CLI instead of exiting with no output.
6
+
7
+ ## 0.3.1
4
8
 
5
9
  - Added upstream synchronization infrastructure with `vercel-labs/skills` kept as a subtree reference under `upstream/vercel-labs-skills`.
6
10
  - Added metadata sanitization, local frontmatter parsing, GitHub blob tree utilities, bounded nested skill discovery, and improved Git source parsing and clone fallback behavior.
@@ -9,6 +13,8 @@
9
13
  - Expanded supported agent targets, including Eve subagents and additional upstream agent definitions, while retaining SafeSkill-specific `flocks` and `kimi-cli` support.
10
14
  - Added agent environment detection via `@vercel/detect-agent`, including banner suppression and non-interactive `find` guidance in detected agent environments.
11
15
  - Updated the test script to exclude subtree and temporary worktree test suites from normal project verification.
16
+ - Fixed SafeSkill hub installs so region fallback is attempted during resolve instead of being blocked by the status endpoint.
17
+ - Added `hermes` as an alias for the `hermes-agent` install target.
12
18
 
13
19
  ## 0.2.2
14
20
 
package/bin/cli.mjs CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  import module from 'node:module';
4
4
 
5
+ process.env.SAFESKILL_CLI_ENTRYPOINT = '1';
6
+
5
7
  // https://nodejs.org/api/module.html#module-compile-cache
6
8
  if (module.enableCompileCache && !process.env.NODE_DISABLE_COMPILE_CACHE) {
7
9
  try {
package/dist/cli.mjs CHANGED
@@ -623,10 +623,8 @@ const REGION_DOMAINS = {
623
623
  global: "safeskill.io"
624
624
  };
625
625
  const REGION_DETECT_URLS = ["https://ifconfig.co/country-iso", "https://ipapi.co/country/"];
626
- const STATUS_TIMEOUT_MS = 2500;
627
626
  const DETECT_TIMEOUT_MS = 1500;
628
627
  let resolvedRegionPromise = null;
629
- let regionAvailabilityCache = /* @__PURE__ */ new Map();
630
628
  let safeSkillRegionOverride = null;
631
629
  function normalizeSafeSkillRegion(region) {
632
630
  const value = region?.trim().toLowerCase();
@@ -662,61 +660,11 @@ async function detectRegionByPublicIP() {
662
660
  }
663
661
  return "global";
664
662
  }
665
- function hasCustomEndpoints() {
666
- return !!(process.env.SAFESKILL_HUB_URL || process.env.SAFESKILL_SEARCH_URL || process.env.SAFESKILL_TELEMETRY_URL || process.env.SKILLS_API_URL);
667
- }
668
663
  function ensureAbsoluteUrl(url, fallbackPath) {
669
664
  const parsed = new URL(url);
670
665
  if (!parsed.pathname || parsed.pathname === "/") parsed.pathname = fallbackPath;
671
666
  return parsed.toString().replace(/\/$/, "");
672
667
  }
673
- function getShellSpecificCommands(variable, value) {
674
- const shell = process.env.SHELL;
675
- const platform = process.platform;
676
- const parentProcess = process.env.PROMPT || process.env.COMSPEC;
677
- const commands = [];
678
- if (platform === "win32") {
679
- process.env.WT_SESSION || process.env.TERM_PROGRAM;
680
- const isPowerShell = process.env.PSModulePath || parentProcess?.includes("powershell.exe") || parentProcess?.includes("pwsh.exe");
681
- const isCMD = parentProcess?.includes("cmd.exe");
682
- if (isCMD && !isPowerShell) commands.push(`CMD: set ${variable}=${value}`);
683
- else if (isPowerShell && !isCMD) commands.push(`PowerShell: $env:${variable}="${value}"`);
684
- else if (isCMD && isPowerShell) {
685
- commands.push(`CMD: set ${variable}=${value}`);
686
- commands.push(`PowerShell: $env:${variable}="${value}"`);
687
- } else {
688
- commands.push(`CMD: set ${variable}=${value}`);
689
- commands.push(`PowerShell: $env:${variable}="${value}"`);
690
- }
691
- } else if (shell?.includes("bash") || shell?.includes("zsh")) commands.push(`bash/zsh: export ${variable}="${value}"`);
692
- else if (shell?.includes("fish")) commands.push(`fish: set -gx ${variable} "${value}"`);
693
- else commands.push(`export ${variable}="${value}"`);
694
- return commands;
695
- }
696
- async function ensureRegionAvailable(region, hubBaseUrl) {
697
- if (hasCustomEndpoints()) return;
698
- const cacheKey = `${region}`;
699
- if (regionAvailabilityCache.has(cacheKey)) {
700
- await regionAvailabilityCache.get(cacheKey);
701
- return;
702
- }
703
- const availabilityPromise = (async () => {
704
- const statusUrl = new URL("/api/v1/status", hubBaseUrl).toString();
705
- try {
706
- if ((await fetch(statusUrl, {
707
- signal: withTimeout(STATUS_TIMEOUT_MS),
708
- headers: getHeaders()
709
- })).ok) return;
710
- } catch {}
711
- const alternateRegion = region === "cn" ? "global" : "cn";
712
- REGION_DOMAINS[alternateRegion];
713
- const currentDomain = REGION_DOMAINS[region];
714
- const commandList = getShellSpecificCommands("SAFESKILL_REGION", alternateRegion).map((cmd) => ` ${cmd}`).join("\n");
715
- throw new Error(`SafeSkill ${region} region (${currentDomain}) is currently unavailable.\nTry these solutions:\n 1. Set environment variable:\n${commandList}\n 2. Or disable telemetry: DISABLE_TELEMETRY=1\n\nFor assistance, contact: dev@safeskill.io`);
716
- })();
717
- regionAvailabilityCache.set(cacheKey, availabilityPromise);
718
- await availabilityPromise;
719
- }
720
668
  async function resolveSafeSkillRegion() {
721
669
  if (resolvedRegionPromise) return resolvedRegionPromise;
722
670
  resolvedRegionPromise = (async () => {
@@ -729,10 +677,7 @@ async function resolveSafeSkillRegion() {
729
677
  }
730
678
  async function getSafeSkillHubBaseUrl() {
731
679
  if (process.env.SAFESKILL_HUB_URL) return process.env.SAFESKILL_HUB_URL;
732
- const region = await resolveSafeSkillRegion();
733
- const baseUrl = `https://hubapi.${REGION_DOMAINS[region]}`;
734
- await ensureRegionAvailable(region, baseUrl);
735
- return baseUrl;
680
+ return `https://hubapi.${REGION_DOMAINS[await resolveSafeSkillRegion()]}`;
736
681
  }
737
682
  async function getSafeSkillSearchUrlWithHub(hubBaseUrl) {
738
683
  const configured = process.env.SAFESKILL_SEARCH_URL || process.env.SKILLS_API_URL;
@@ -972,7 +917,7 @@ async function resolveHubSkill(source, agent) {
972
917
  source,
973
918
  agent
974
919
  })
975
- }) : null;
920
+ }).catch(() => null) : null;
976
921
  return resolveWithAgent && resolveWithAgent.ok ? resolveWithAgent : fetch(resolveUrl, {
977
922
  method: "POST",
978
923
  headers: getHeaders({ "content-type": "application/json" }),
@@ -980,15 +925,25 @@ async function resolveHubSkill(source, agent) {
980
925
  });
981
926
  };
982
927
  let resolvedBaseUrl = baseUrl;
983
- let response = await resolveFromBaseUrl(baseUrl);
984
- const alternateBaseUrl = response.status >= 500 ? getAlternateHubBaseUrl(baseUrl) : null;
928
+ let response = null;
929
+ let lastError;
930
+ try {
931
+ response = await resolveFromBaseUrl(baseUrl);
932
+ } catch (error) {
933
+ lastError = error;
934
+ }
935
+ const alternateBaseUrl = !response || response.status >= 500 ? getAlternateHubBaseUrl(baseUrl) : null;
985
936
  if (alternateBaseUrl) {
986
- const alternateResponse = await resolveFromBaseUrl(alternateBaseUrl);
987
- if (alternateResponse.ok || !response.ok) {
937
+ const alternateResponse = await resolveFromBaseUrl(alternateBaseUrl).catch((error) => {
938
+ lastError = error;
939
+ return null;
940
+ });
941
+ if (alternateResponse && (alternateResponse.ok || !response || !response.ok)) {
988
942
  response = alternateResponse;
989
943
  resolvedBaseUrl = alternateBaseUrl;
990
944
  }
991
945
  }
946
+ if (!response) throw lastError instanceof Error ? lastError : /* @__PURE__ */ new Error("hub resolve failed");
992
947
  if (!response.ok) throw new Error(`hub resolve failed: ${response.status}`);
993
948
  const resolved = await readEnvelope(response);
994
949
  resolved.hubBaseUrl = resolvedBaseUrl;
@@ -2080,6 +2035,21 @@ const agents = {
2080
2035
  detectInstalled: async () => false
2081
2036
  }
2082
2037
  };
2038
+ const agentAliases = { hermes: "hermes-agent" };
2039
+ function normalizeAgentType(input) {
2040
+ if (input in agents) return input;
2041
+ return agentAliases[input] ?? null;
2042
+ }
2043
+ function normalizeAgentTypes(inputs) {
2044
+ const normalized = inputs.map((agent) => ({
2045
+ input: agent,
2046
+ normalized: normalizeAgentType(agent)
2047
+ }));
2048
+ return {
2049
+ normalizedAgents: normalized.filter((agent) => Boolean(agent.normalized)).map((agent) => agent.normalized),
2050
+ invalidAgents: normalized.filter((agent) => !agent.normalized).map((agent) => agent.input)
2051
+ };
2052
+ }
2083
2053
  async function detectInstalledAgents() {
2084
2054
  return (await Promise.all(Object.entries(agents).map(async ([type, config]) => ({
2085
2055
  type,
@@ -2930,7 +2900,7 @@ async function saveSelectedAgents(agents) {
2930
2900
  lock.lastSelectedAgents = agents;
2931
2901
  await writeSkillLock(lock);
2932
2902
  }
2933
- var version$1 = "0.3.0";
2903
+ var version$1 = "0.3.2";
2934
2904
  const isCancelled$1 = (value) => typeof value === "symbol";
2935
2905
  async function isSourcePrivate(source) {
2936
2906
  const ownerRepo = parseOwnerRepo(source);
@@ -3169,13 +3139,13 @@ async function handleWellKnownSkills(source, url, options, spinner) {
3169
3139
  targetAgents = validAgents;
3170
3140
  M.info(`Installing to all ${targetAgents.length} agents`);
3171
3141
  } else if (options.agent && options.agent.length > 0) {
3172
- const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
3142
+ const { normalizedAgents, invalidAgents } = normalizeAgentTypes(options.agent);
3173
3143
  if (invalidAgents.length > 0) {
3174
3144
  M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
3175
3145
  M.info(`Valid agents: ${validAgents.join(", ")}`);
3176
3146
  process.exit(1);
3177
3147
  }
3178
- targetAgents = options.agent;
3148
+ targetAgents = normalizedAgents;
3179
3149
  } else {
3180
3150
  spinner.start("Loading agents...");
3181
3151
  const installedAgents = await detectInstalledAgents();
@@ -3581,14 +3551,14 @@ async function runAdd(args, options = {}) {
3581
3551
  targetAgents = validAgents;
3582
3552
  M.info(`Installing to all ${targetAgents.length} agents`);
3583
3553
  } else if (options.agent && options.agent.length > 0) {
3584
- const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
3554
+ const { normalizedAgents, invalidAgents } = normalizeAgentTypes(options.agent);
3585
3555
  if (invalidAgents.length > 0) {
3586
3556
  M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
3587
3557
  M.info(`Valid agents: ${validAgents.join(", ")}`);
3588
3558
  await cleanup(tempDir);
3589
3559
  process.exit(1);
3590
3560
  }
3591
- targetAgents = options.agent;
3561
+ targetAgents = normalizedAgents;
3592
3562
  } else {
3593
3563
  spinner.start("Loading agents...");
3594
3564
  const installedAgents = await detectInstalledAgents();
@@ -4364,13 +4334,13 @@ async function runSync(args, options = {}) {
4364
4334
  targetAgents = validAgents;
4365
4335
  M.info(`Installing to all ${targetAgents.length} agents`);
4366
4336
  } else if (options.agent && options.agent.length > 0) {
4367
- const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
4337
+ const { normalizedAgents, invalidAgents } = normalizeAgentTypes(options.agent);
4368
4338
  if (invalidAgents.length > 0) {
4369
4339
  M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
4370
4340
  M.info(`Valid agents: ${validAgents.join(", ")}`);
4371
4341
  process.exit(1);
4372
4342
  }
4373
- targetAgents = options.agent;
4343
+ targetAgents = normalizedAgents;
4374
4344
  } else {
4375
4345
  spinner.start("Loading agents...");
4376
4346
  const installedAgents = await detectInstalledAgents();
@@ -4614,13 +4584,13 @@ async function runList(args) {
4614
4584
  let agentFilter;
4615
4585
  if (options.agent && options.agent.length > 0) {
4616
4586
  const validAgents = Object.keys(agents);
4617
- const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
4587
+ const { normalizedAgents, invalidAgents } = normalizeAgentTypes(options.agent);
4618
4588
  if (invalidAgents.length > 0) {
4619
4589
  console.log(`${YELLOW}Invalid agents: ${invalidAgents.join(", ")}${RESET$2}`);
4620
4590
  console.log(`${DIM$2}Valid agents: ${validAgents.join(", ")}${RESET$2}`);
4621
4591
  process.exit(1);
4622
4592
  }
4623
- agentFilter = options.agent;
4593
+ agentFilter = normalizedAgents;
4624
4594
  }
4625
4595
  const installedSkills = await listInstalledSkills({
4626
4596
  global: scope,
@@ -4715,14 +4685,16 @@ async function removeCommand(skillNames, options) {
4715
4685
  Se(import_picocolors.default.yellow("No skills found to remove."));
4716
4686
  return;
4717
4687
  }
4688
+ let normalizedAgentFilter;
4718
4689
  if (options.agent && options.agent.length > 0) {
4719
4690
  const validAgents = Object.keys(agents);
4720
- const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
4691
+ const { normalizedAgents, invalidAgents } = normalizeAgentTypes(options.agent);
4721
4692
  if (invalidAgents.length > 0) {
4722
4693
  M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
4723
4694
  M.info(`Valid agents: ${validAgents.join(", ")}`);
4724
4695
  process.exit(1);
4725
4696
  }
4697
+ normalizedAgentFilter = normalizedAgents;
4726
4698
  }
4727
4699
  let selectedSkills = [];
4728
4700
  if (options.all) selectedSkills = installedSkills;
@@ -4749,7 +4721,7 @@ async function removeCommand(skillNames, options) {
4749
4721
  selectedSkills = selected;
4750
4722
  }
4751
4723
  let targetAgents;
4752
- if (options.agent && options.agent.length > 0) targetAgents = options.agent;
4724
+ if (normalizedAgentFilter && normalizedAgentFilter.length > 0) targetAgents = normalizedAgentFilter;
4753
4725
  else {
4754
4726
  targetAgents = Object.keys(agents);
4755
4727
  spinner.stop(`Targeting ${targetAgents.length} potential agent(s)`);
@@ -6762,6 +6734,7 @@ async function main() {
6762
6734
  }
6763
6735
  }
6764
6736
  function isCliEntrypoint() {
6737
+ if (process.env.SAFESKILL_CLI_ENTRYPOINT === "1") return true;
6765
6738
  const entrypoint = process.argv[1];
6766
6739
  return entrypoint ? import.meta.url === pathToFileURL(entrypoint).href : false;
6767
6740
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@safeskill/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "SafeSkill CLI, for safe your skills",
5
5
  "type": "module",
6
6
  "bin": {