skillhub 0.1.6 → 0.1.8

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 (2) hide show
  1. package/dist/index.js +100 -34
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -25,7 +25,7 @@ import { parseSkillMd } from "skillhub-core";
25
25
  import https from "https";
26
26
  import http from "http";
27
27
  var API_BASE_URL = process.env.SKILLHUB_API_URL || "https://skills.palebluedot.live/api";
28
- var API_TIMEOUT = parseInt(process.env.SKILLHUB_API_TIMEOUT || "30000");
28
+ var API_TIMEOUT = parseInt(process.env.SKILLHUB_API_TIMEOUT || "10000");
29
29
  function httpsRequest(url, options = {}) {
30
30
  return new Promise((resolve, reject) => {
31
31
  const parsedUrl = new URL(url);
@@ -135,6 +135,7 @@ async function trackInstall(skillId, platform, method = "cli") {
135
135
 
136
136
  // src/utils/github.ts
137
137
  import { Octokit } from "@octokit/rest";
138
+ import https2 from "https";
138
139
  var octokit = null;
139
140
  function getOctokit() {
140
141
  if (!octokit) {
@@ -149,25 +150,73 @@ function getOctokit() {
149
150
  }
150
151
  return octokit;
151
152
  }
153
+ async function fetchRawFile(owner, repo, path3, branch) {
154
+ return new Promise((resolve, reject) => {
155
+ const url = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${path3}`;
156
+ https2.get(url, { timeout: 1e4 }, (res) => {
157
+ if (res.statusCode === 404) {
158
+ reject(new Error("File not found"));
159
+ return;
160
+ }
161
+ if (res.statusCode !== 200) {
162
+ reject(new Error(`HTTP ${res.statusCode}`));
163
+ return;
164
+ }
165
+ let data = "";
166
+ res.on("data", (chunk) => data += chunk);
167
+ res.on("end", () => resolve(data));
168
+ }).on("error", (err) => {
169
+ reject(err);
170
+ }).on("timeout", () => {
171
+ reject(new Error("Request timeout"));
172
+ });
173
+ });
174
+ }
152
175
  async function fetchSkillContent(owner, repo, skillPath, branch = "main") {
153
176
  const client = getOctokit();
154
177
  const skillMdPath = skillPath ? `${skillPath}/SKILL.md` : "SKILL.md";
155
178
  let skillMdResponse;
156
- try {
157
- skillMdResponse = await client.repos.getContent({
158
- owner,
159
- repo,
160
- path: skillMdPath,
161
- ref: branch
162
- });
163
- } catch (error) {
164
- if (error.status === 404) {
165
- throw new Error(`SKILL.md not found at ${owner}/${repo}/${skillMdPath}`);
166
- }
167
- if (error.message?.includes("timeout")) {
168
- throw new Error(`GitHub request timeout. Check your internet connection or try again later.`);
179
+ let lastError;
180
+ const pathsToTry = [
181
+ skillMdPath,
182
+ // Common skill directories
183
+ ...skillPath && !skillPath.startsWith("skills/") ? [`skills/${skillPath}/SKILL.md`] : [],
184
+ ...skillPath && !skillPath.startsWith(".claude/") ? [`.claude/skills/${skillPath}/SKILL.md`] : [],
185
+ ...skillPath && !skillPath.startsWith(".github/") ? [`.github/skills/${skillPath}/SKILL.md`] : []
186
+ ];
187
+ for (const pathToTry of pathsToTry) {
188
+ try {
189
+ skillMdResponse = await client.repos.getContent({
190
+ owner,
191
+ repo,
192
+ path: pathToTry,
193
+ ref: branch
194
+ });
195
+ break;
196
+ } catch (error) {
197
+ lastError = error;
198
+ if (error.message?.includes("timeout") || error.message?.includes("network")) {
199
+ try {
200
+ const rawContent = await fetchRawFile(owner, repo, pathToTry, branch);
201
+ skillMdResponse = {
202
+ data: {
203
+ content: Buffer.from(rawContent).toString("base64"),
204
+ encoding: "base64"
205
+ }
206
+ };
207
+ break;
208
+ } catch (rawError) {
209
+ throw new Error(`GitHub API timeout. Try using --no-api flag or check your network connection.`);
210
+ }
211
+ }
212
+ if (error.status === 404) {
213
+ continue;
214
+ }
215
+ throw new Error(`Failed to fetch from GitHub: ${error.message}`);
169
216
  }
170
- throw new Error(`Failed to fetch from GitHub: ${error.message}`);
217
+ }
218
+ if (!skillMdResponse) {
219
+ throw new Error(`SKILL.md not found at ${owner}/${repo} (tried ${pathsToTry.length} paths)`);
171
220
  }
172
221
  if (!("content" in skillMdResponse.data)) {
173
222
  throw new Error("SKILL.md not found");
@@ -247,7 +296,7 @@ async function getDefaultBranch(owner, repo) {
247
296
 
248
297
  // src/commands/install.ts
249
298
  async function install(skillId, options) {
250
- const spinner = ora("Fetching skill information...").start();
299
+ const spinner = ora("Parsing skill ID...").start();
251
300
  try {
252
301
  const parts = skillId.split("/");
253
302
  if (parts.length < 2) {
@@ -257,11 +306,20 @@ async function install(skillId, options) {
257
306
  const [owner, repo, ...rest] = parts;
258
307
  let skillPath = rest.join("/");
259
308
  let skillInfo;
260
- try {
261
- skillInfo = await getSkill(skillId);
262
- } catch (error) {
263
- spinner.warn(`API error: ${error.message}`);
264
- spinner.start("Falling back to direct GitHub fetch...");
309
+ if (!options.noApi) {
310
+ spinner.text = `Connecting to ${process.env.SKILLHUB_API_URL || "https://skills.palebluedot.live"}...`;
311
+ try {
312
+ skillInfo = await getSkill(skillId);
313
+ if (skillInfo) {
314
+ spinner.succeed(`Found in registry: ${skillInfo.name}`);
315
+ spinner.start("Preparing installation...");
316
+ }
317
+ } catch (error) {
318
+ spinner.warn(`API unavailable: ${error.message}`);
319
+ spinner.start("Falling back to direct GitHub fetch...");
320
+ }
321
+ } else {
322
+ spinner.text = "Skipping API lookup (--no-api flag)";
265
323
  }
266
324
  let skillName;
267
325
  let branch = "main";
@@ -580,11 +638,15 @@ var pkg = require2("../package.json");
580
638
  var VERSION = pkg.version;
581
639
  var program = new Command();
582
640
  program.name("skillhub").description("CLI for managing AI Agent skills").version(VERSION);
583
- program.command("install <skill-id>").description("Install a skill from the registry").option("-p, --platform <platform>", "Target platform (claude, codex, copilot, cursor, windsurf)", "claude").option("--project", "Install in the current project instead of globally").option("-f, --force", "Overwrite existing skill").action(async (skillId, options) => {
641
+ program.command("install <skill-id>").description("Install a skill from the registry").option("-p, --platform <platform>", "Target platform (claude, codex, copilot, cursor, windsurf)").option("--project", "Install in the current project instead of globally").option("-f, --force", "Overwrite existing skill").option("--no-api", "Skip API lookup and fetch directly from GitHub").action(async (skillId, options) => {
642
+ const userConfig = await loadConfig();
643
+ const platform = options.platform || userConfig.defaultPlatform || "claude";
584
644
  await install(skillId, {
585
- platform: options.platform,
645
+ platform,
586
646
  project: options.project,
587
- force: options.force
647
+ force: options.force,
648
+ noApi: !options.api
649
+ // Commander converts --no-api to api: false
588
650
  });
589
651
  });
590
652
  program.command("search <query>").description("Search for skills in the registry").option("-p, --platform <platform>", "Filter by platform").option("-l, --limit <number>", "Number of results", "10").action(async (query, options) => {
@@ -596,31 +658,35 @@ program.command("list").description("List installed skills").option("-p, --platf
596
658
  program.command("config").description("Manage CLI configuration").option("--set <key=value>", "Set a configuration value").option("--get <key>", "Get a configuration value").option("--list", "List all configuration values").action(async (options) => {
597
659
  await config(options);
598
660
  });
599
- program.command("uninstall <skill-name>").description("Uninstall a skill").option("-p, --platform <platform>", "Target platform", "claude").option("--project", "Uninstall from project instead of globally").action(async (skillName, options) => {
661
+ program.command("uninstall <skill-name>").description("Uninstall a skill").option("-p, --platform <platform>", "Target platform").option("--project", "Uninstall from project instead of globally").action(async (skillName, options) => {
600
662
  const fs3 = await import("fs-extra");
601
663
  const { getSkillPath: getSkillPath2, isSkillInstalled: isSkillInstalled2 } = await import("./paths-BVI5WSHE.js");
602
- const installed = await isSkillInstalled2(options.platform, skillName, options.project);
664
+ const userConfig = await loadConfig();
665
+ const platform = options.platform || userConfig.defaultPlatform || "claude";
666
+ const installed = await isSkillInstalled2(platform, skillName, options.project);
603
667
  if (!installed) {
604
668
  console.log(chalk5.yellow(`Skill ${skillName} is not installed.`));
605
669
  process.exit(1);
606
670
  }
607
- const skillPath = getSkillPath2(options.platform, skillName, options.project);
671
+ const skillPath = getSkillPath2(platform, skillName, options.project);
608
672
  await fs3.default.remove(skillPath);
609
673
  console.log(chalk5.green(`Skill ${skillName} uninstalled successfully.`));
610
674
  });
611
- program.command("update [skill-name]").description("Update installed skills").option("-p, --platform <platform>", "Target platform", "claude").option("--all", "Update all installed skills").action(async (skillName, options) => {
675
+ program.command("update [skill-name]").description("Update installed skills").option("-p, --platform <platform>", "Target platform").option("--all", "Update all installed skills").action(async (skillName, options) => {
612
676
  const fsExtra = await import("fs-extra");
613
677
  const pathModule = await import("path");
614
678
  const { getSkillsPath: getSkillsPath2, getSkillPath: getSkillPath2 } = await import("./paths-BVI5WSHE.js");
679
+ const userConfig = await loadConfig();
680
+ const platform = options.platform || userConfig.defaultPlatform || "claude";
615
681
  const ALL_PLATFORMS2 = ["claude", "codex", "copilot", "cursor", "windsurf"];
616
682
  if (options.all) {
617
683
  console.log(chalk5.cyan("\nUpdating all installed skills...\n"));
618
- const platforms = options.platform ? [options.platform] : ALL_PLATFORMS2;
684
+ const platforms = options.platform ? [platform] : ALL_PLATFORMS2;
619
685
  let updated = 0;
620
686
  let failed = 0;
621
687
  let skipped = 0;
622
- for (const platform of platforms) {
623
- const skillsPath = getSkillsPath2(platform);
688
+ for (const p of platforms) {
689
+ const skillsPath = getSkillsPath2(p);
624
690
  if (!await fsExtra.default.pathExists(skillsPath)) {
625
691
  continue;
626
692
  }
@@ -644,7 +710,7 @@ program.command("update [skill-name]").description("Update installed skills").op
644
710
  }
645
711
  console.log(chalk5.dim(` Updating ${entry.name}...`));
646
712
  await install(skillId, {
647
- platform,
713
+ platform: p,
648
714
  force: true
649
715
  });
650
716
  updated++;
@@ -664,7 +730,7 @@ program.command("update [skill-name]").description("Update installed skills").op
664
730
  console.log(chalk5.red("Please specify a skill name or use --all."));
665
731
  process.exit(1);
666
732
  }
667
- const skillPath = getSkillPath2(options.platform, skillName);
733
+ const skillPath = getSkillPath2(platform, skillName);
668
734
  const metadataPath = pathModule.join(skillPath, ".skillhub.json");
669
735
  if (!await fsExtra.default.pathExists(metadataPath)) {
670
736
  console.log(chalk5.yellow(`Skill ${skillName} was not installed via CLI.`));
@@ -680,7 +746,7 @@ program.command("update [skill-name]").description("Update installed skills").op
680
746
  }
681
747
  console.log(chalk5.dim(`Updating ${skillName}...`));
682
748
  await install(skillId, {
683
- platform: options.platform,
749
+ platform,
684
750
  force: true
685
751
  });
686
752
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillhub",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "CLI tool for managing AI Agent skills - search, install, and update skills for Claude, Codex, Copilot, and more",
5
5
  "author": "SkillHub Contributors",
6
6
  "license": "MIT",