skillhub 0.1.7 → 0.1.9
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 +103 -26
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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");
|
|
@@ -322,6 +371,28 @@ async function install(skillId, options) {
|
|
|
322
371
|
}
|
|
323
372
|
const actualName = parsed.metadata.name || skillName;
|
|
324
373
|
const installPath = getSkillPath(options.platform, actualName, options.project);
|
|
374
|
+
const metadataPath = path.join(installPath, ".skillhub.json");
|
|
375
|
+
if (await fs.pathExists(metadataPath)) {
|
|
376
|
+
try {
|
|
377
|
+
const existingMetadata = await fs.readJson(metadataPath);
|
|
378
|
+
if (existingMetadata.skillId && existingMetadata.skillId !== skillId) {
|
|
379
|
+
spinner.warn(`Name collision detected!`);
|
|
380
|
+
console.log(chalk.yellow(`
|
|
381
|
+
A different skill is already installed with the name "${actualName}":`));
|
|
382
|
+
console.log(chalk.dim(` Existing: ${existingMetadata.skillId}`));
|
|
383
|
+
console.log(chalk.dim(` New: ${skillId}`));
|
|
384
|
+
console.log();
|
|
385
|
+
if (!options.force) {
|
|
386
|
+
console.log(chalk.red("Installation cancelled to prevent overwriting."));
|
|
387
|
+
console.log(chalk.dim("Use --force to overwrite the existing skill."));
|
|
388
|
+
process.exit(1);
|
|
389
|
+
} else {
|
|
390
|
+
console.log(chalk.yellow("Overwriting existing skill (--force flag used).\n"));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
}
|
|
325
396
|
if (installed && options.force) {
|
|
326
397
|
await fs.remove(installPath);
|
|
327
398
|
}
|
|
@@ -589,9 +660,11 @@ var pkg = require2("../package.json");
|
|
|
589
660
|
var VERSION = pkg.version;
|
|
590
661
|
var program = new Command();
|
|
591
662
|
program.name("skillhub").description("CLI for managing AI Agent skills").version(VERSION);
|
|
592
|
-
program.command("install <skill-id>").description("Install a skill from the registry").option("-p, --platform <platform>", "Target platform (claude, codex, copilot, cursor, windsurf)"
|
|
663
|
+
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) => {
|
|
664
|
+
const userConfig = await loadConfig();
|
|
665
|
+
const platform = options.platform || userConfig.defaultPlatform || "claude";
|
|
593
666
|
await install(skillId, {
|
|
594
|
-
platform
|
|
667
|
+
platform,
|
|
595
668
|
project: options.project,
|
|
596
669
|
force: options.force,
|
|
597
670
|
noApi: !options.api
|
|
@@ -607,31 +680,35 @@ program.command("list").description("List installed skills").option("-p, --platf
|
|
|
607
680
|
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) => {
|
|
608
681
|
await config(options);
|
|
609
682
|
});
|
|
610
|
-
program.command("uninstall <skill-name>").description("Uninstall a skill").option("-p, --platform <platform>", "Target platform"
|
|
683
|
+
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) => {
|
|
611
684
|
const fs3 = await import("fs-extra");
|
|
612
685
|
const { getSkillPath: getSkillPath2, isSkillInstalled: isSkillInstalled2 } = await import("./paths-BVI5WSHE.js");
|
|
613
|
-
const
|
|
686
|
+
const userConfig = await loadConfig();
|
|
687
|
+
const platform = options.platform || userConfig.defaultPlatform || "claude";
|
|
688
|
+
const installed = await isSkillInstalled2(platform, skillName, options.project);
|
|
614
689
|
if (!installed) {
|
|
615
690
|
console.log(chalk5.yellow(`Skill ${skillName} is not installed.`));
|
|
616
691
|
process.exit(1);
|
|
617
692
|
}
|
|
618
|
-
const skillPath = getSkillPath2(
|
|
693
|
+
const skillPath = getSkillPath2(platform, skillName, options.project);
|
|
619
694
|
await fs3.default.remove(skillPath);
|
|
620
695
|
console.log(chalk5.green(`Skill ${skillName} uninstalled successfully.`));
|
|
621
696
|
});
|
|
622
|
-
program.command("update [skill-name]").description("Update installed skills").option("-p, --platform <platform>", "Target platform"
|
|
697
|
+
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) => {
|
|
623
698
|
const fsExtra = await import("fs-extra");
|
|
624
699
|
const pathModule = await import("path");
|
|
625
700
|
const { getSkillsPath: getSkillsPath2, getSkillPath: getSkillPath2 } = await import("./paths-BVI5WSHE.js");
|
|
701
|
+
const userConfig = await loadConfig();
|
|
702
|
+
const platform = options.platform || userConfig.defaultPlatform || "claude";
|
|
626
703
|
const ALL_PLATFORMS2 = ["claude", "codex", "copilot", "cursor", "windsurf"];
|
|
627
704
|
if (options.all) {
|
|
628
705
|
console.log(chalk5.cyan("\nUpdating all installed skills...\n"));
|
|
629
|
-
const platforms = options.platform ? [
|
|
706
|
+
const platforms = options.platform ? [platform] : ALL_PLATFORMS2;
|
|
630
707
|
let updated = 0;
|
|
631
708
|
let failed = 0;
|
|
632
709
|
let skipped = 0;
|
|
633
|
-
for (const
|
|
634
|
-
const skillsPath = getSkillsPath2(
|
|
710
|
+
for (const p of platforms) {
|
|
711
|
+
const skillsPath = getSkillsPath2(p);
|
|
635
712
|
if (!await fsExtra.default.pathExists(skillsPath)) {
|
|
636
713
|
continue;
|
|
637
714
|
}
|
|
@@ -655,7 +732,7 @@ program.command("update [skill-name]").description("Update installed skills").op
|
|
|
655
732
|
}
|
|
656
733
|
console.log(chalk5.dim(` Updating ${entry.name}...`));
|
|
657
734
|
await install(skillId, {
|
|
658
|
-
platform,
|
|
735
|
+
platform: p,
|
|
659
736
|
force: true
|
|
660
737
|
});
|
|
661
738
|
updated++;
|
|
@@ -675,7 +752,7 @@ program.command("update [skill-name]").description("Update installed skills").op
|
|
|
675
752
|
console.log(chalk5.red("Please specify a skill name or use --all."));
|
|
676
753
|
process.exit(1);
|
|
677
754
|
}
|
|
678
|
-
const skillPath = getSkillPath2(
|
|
755
|
+
const skillPath = getSkillPath2(platform, skillName);
|
|
679
756
|
const metadataPath = pathModule.join(skillPath, ".skillhub.json");
|
|
680
757
|
if (!await fsExtra.default.pathExists(metadataPath)) {
|
|
681
758
|
console.log(chalk5.yellow(`Skill ${skillName} was not installed via CLI.`));
|
|
@@ -691,7 +768,7 @@ program.command("update [skill-name]").description("Update installed skills").op
|
|
|
691
768
|
}
|
|
692
769
|
console.log(chalk5.dim(`Updating ${skillName}...`));
|
|
693
770
|
await install(skillId, {
|
|
694
|
-
platform
|
|
771
|
+
platform,
|
|
695
772
|
force: true
|
|
696
773
|
});
|
|
697
774
|
} catch (error) {
|
package/package.json
CHANGED