skillhub 0.2.0 → 0.2.1

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 +55 -17
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import fs from "fs-extra";
21
21
  import * as path from "path";
22
22
  import chalk from "chalk";
23
23
  import ora from "ora";
24
- import { parseSkillMd } from "skillhub-core";
24
+ import { parseSkillMd, parseGenericInstructionFile, INSTRUCTION_FILE_PATTERNS as INSTRUCTION_FILE_PATTERNS2 } from "skillhub-core";
25
25
 
26
26
  // src/utils/api.ts
27
27
  import https from "https";
@@ -156,6 +156,7 @@ async function getSkillFiles(id) {
156
156
 
157
157
  // src/utils/github.ts
158
158
  import { Octokit } from "@octokit/rest";
159
+ import { INSTRUCTION_FILE_PATTERNS } from "skillhub-core";
159
160
  import https2 from "https";
160
161
  var octokit = null;
161
162
  function getOctokit() {
@@ -193,17 +194,34 @@ async function fetchRawFile(owner, repo, path3, branch) {
193
194
  });
194
195
  });
195
196
  }
196
- async function fetchSkillContent(owner, repo, skillPath, branch = "main") {
197
+ function getInstructionFilename(sourceFormat) {
198
+ const pattern = INSTRUCTION_FILE_PATTERNS.find((p) => p.format === sourceFormat);
199
+ return pattern?.filename || "SKILL.md";
200
+ }
201
+ async function fetchSkillContent(owner, repo, skillPath, branch = "main", sourceFormat = "skill.md") {
197
202
  const client = getOctokit();
198
- const skillMdPath = skillPath ? `${skillPath}/SKILL.md` : "SKILL.md";
203
+ const filename = getInstructionFilename(sourceFormat);
204
+ const isStandalone = sourceFormat !== "skill.md";
205
+ const basePath = skillPath ? `${skillPath}/${filename}` : filename;
199
206
  let skillMdResponse;
200
- const pathsToTry = [
201
- skillMdPath,
202
- // Common skill directories
203
- ...skillPath && !skillPath.startsWith("skills/") ? [`skills/${skillPath}/SKILL.md`] : [],
204
- ...skillPath && !skillPath.startsWith(".claude/") ? [`.claude/skills/${skillPath}/SKILL.md`] : [],
205
- ...skillPath && !skillPath.startsWith(".github/") ? [`.github/skills/${skillPath}/SKILL.md`] : []
206
- ];
207
+ let pathsToTry;
208
+ if (sourceFormat === "cursorrules" || sourceFormat === "windsurfrules") {
209
+ pathsToTry = [filename];
210
+ } else if (sourceFormat === "copilot-instructions") {
211
+ pathsToTry = [`.github/${filename}`];
212
+ } else if (sourceFormat === "agents.md") {
213
+ pathsToTry = [basePath];
214
+ if (skillPath) {
215
+ pathsToTry.push(filename);
216
+ }
217
+ } else {
218
+ pathsToTry = [
219
+ basePath,
220
+ ...skillPath && !skillPath.startsWith("skills/") ? [`skills/${skillPath}/SKILL.md`] : [],
221
+ ...skillPath && !skillPath.startsWith(".claude/") ? [`.claude/skills/${skillPath}/SKILL.md`] : [],
222
+ ...skillPath && !skillPath.startsWith(".github/") ? [`.github/skills/${skillPath}/SKILL.md`] : []
223
+ ];
224
+ }
207
225
  for (const pathToTry of pathsToTry) {
208
226
  try {
209
227
  skillMdResponse = await client.repos.getContent({
@@ -235,12 +253,20 @@ async function fetchSkillContent(owner, repo, skillPath, branch = "main") {
235
253
  }
236
254
  }
237
255
  if (!skillMdResponse) {
238
- throw new Error(`SKILL.md not found at ${owner}/${repo} (tried ${pathsToTry.length} paths)`);
256
+ throw new Error(`${filename} not found at ${owner}/${repo} (tried ${pathsToTry.length} paths)`);
239
257
  }
240
258
  if (!("content" in skillMdResponse.data)) {
241
- throw new Error("SKILL.md not found");
259
+ throw new Error(`${filename} not found`);
242
260
  }
243
261
  const skillMd = Buffer.from(skillMdResponse.data.content, "base64").toString("utf-8");
262
+ if (isStandalone) {
263
+ return {
264
+ skillMd,
265
+ scripts: [],
266
+ references: [],
267
+ assets: []
268
+ };
269
+ }
244
270
  const scripts = [];
245
271
  try {
246
272
  const scriptsPath = skillPath ? `${skillPath}/scripts` : "scripts";
@@ -444,10 +470,12 @@ async function install(skillId, options) {
444
470
  }
445
471
  let skillName;
446
472
  let branch = "main";
473
+ let sourceFormat = "skill.md";
447
474
  if (skillInfo) {
448
475
  skillName = skillInfo.name;
449
476
  skillPath = skillInfo.skillPath;
450
477
  branch = skillInfo.branch || "main";
478
+ sourceFormat = skillInfo.sourceFormat || "skill.md";
451
479
  spinner.text = `Found skill: ${chalk.cyan(skillName)}`;
452
480
  } else {
453
481
  spinner.text = "Skill not in registry, fetching from GitHub...";
@@ -476,7 +504,10 @@ async function install(skillId, options) {
476
504
  spinner.text = "Downloading skill files...";
477
505
  const cachedFiles = await getSkillFiles(skillInfo.id);
478
506
  if (cachedFiles && cachedFiles.files.length > 0) {
479
- content = convertCachedFilesToSkillContent(cachedFiles);
507
+ if (cachedFiles.sourceFormat) {
508
+ sourceFormat = cachedFiles.sourceFormat;
509
+ }
510
+ content = convertCachedFilesToSkillContent(cachedFiles, sourceFormat);
480
511
  spinner.text = cachedFiles.fromCache ? `Using cached files (${cachedFiles.files.length} files)` : `Downloaded ${cachedFiles.files.length} files via API`;
481
512
  }
482
513
  } catch {
@@ -490,7 +521,7 @@ async function install(skillId, options) {
490
521
  spinner.text = `Downloading from GitHub: ${owner}/${repo}/${skillPath || ""}...`;
491
522
  }
492
523
  try {
493
- content = await fetchSkillContent(owner, repo, skillPath, branch);
524
+ content = await fetchSkillContent(owner, repo, skillPath, branch, sourceFormat);
494
525
  spinner.text = `Downloaded ${content.scripts.length} scripts, ${content.references.length} references`;
495
526
  } catch (error) {
496
527
  spinner.fail("Failed to download skill files");
@@ -512,7 +543,11 @@ async function install(skillId, options) {
512
543
  spinner.fail("Failed to download skill content");
513
544
  process.exit(1);
514
545
  }
515
- const parsed = parseSkillMd(content.skillMd);
546
+ const parsed = sourceFormat === "skill.md" ? parseSkillMd(content.skillMd) : parseGenericInstructionFile(content.skillMd, sourceFormat, {
547
+ name: skillName,
548
+ description: skillInfo?.description || null,
549
+ owner
550
+ });
516
551
  if (!parsed.validation.isValid) {
517
552
  spinner.warn("Skill has validation issues:");
518
553
  for (const error of parsed.validation.errors) {
@@ -650,13 +685,16 @@ function getPlatformSetupInstructions(platform, installPath) {
650
685
  return null;
651
686
  }
652
687
  }
653
- function convertCachedFilesToSkillContent(response) {
688
+ var MAIN_FILE_NAMES = INSTRUCTION_FILE_PATTERNS2.map((p) => p.filename);
689
+ function convertCachedFilesToSkillContent(response, sourceFormat = "skill.md") {
654
690
  let skillMd = "";
655
691
  const scripts = [];
656
692
  const references = [];
693
+ const expectedPattern = INSTRUCTION_FILE_PATTERNS2.find((p) => p.format === sourceFormat);
694
+ const expectedFilename = expectedPattern?.filename || "SKILL.md";
657
695
  for (const file of response.files) {
658
696
  if (!file.content) continue;
659
- if (file.name === "SKILL.md" && (file.path === "SKILL.md" || file.path === file.name)) {
697
+ if (!skillMd && (file.name === expectedFilename || MAIN_FILE_NAMES.includes(file.name)) && file.path === file.name) {
660
698
  skillMd = file.content;
661
699
  continue;
662
700
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillhub",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
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",