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.
- package/dist/index.js +100 -34
- 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 || "
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
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("
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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)"
|
|
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
|
|
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"
|
|
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
|
|
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(
|
|
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"
|
|
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 ? [
|
|
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
|
|
623
|
-
const skillsPath = getSkillsPath2(
|
|
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(
|
|
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
|
|
749
|
+
platform,
|
|
684
750
|
force: true
|
|
685
751
|
});
|
|
686
752
|
} catch (error) {
|
package/package.json
CHANGED