skillhub 0.2.4 → 0.2.6
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 +90 -16
- package/package.json +61 -61
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -3516,6 +3516,12 @@ var VALID_PLATFORMS = ["claude", "codex", "copilot", "cursor", "windsurf"];
|
|
|
3516
3516
|
var NAME_PATTERN = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
|
|
3517
3517
|
var MAX_DESCRIPTION_LENGTH = 1024;
|
|
3518
3518
|
var MAX_NAME_LENGTH = 64;
|
|
3519
|
+
function sanitizeUtf8(input) {
|
|
3520
|
+
let result = input.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "");
|
|
3521
|
+
result = Buffer.from(result, "utf8").toString("utf8");
|
|
3522
|
+
result = result.replace(/[\uD800-\uDFFF]/g, "");
|
|
3523
|
+
return result;
|
|
3524
|
+
}
|
|
3519
3525
|
var INSTRUCTION_FILE_PATTERNS = [
|
|
3520
3526
|
{
|
|
3521
3527
|
format: "skill.md",
|
|
@@ -3600,8 +3606,8 @@ function parseSkillMd(content) {
|
|
|
3600
3606
|
validateTriggers(frontmatter.triggers, warnings);
|
|
3601
3607
|
}
|
|
3602
3608
|
const metadata = {
|
|
3603
|
-
name: String(frontmatter.name || ""),
|
|
3604
|
-
description: String(frontmatter.description || ""),
|
|
3609
|
+
name: sanitizeUtf8(String(frontmatter.name || "")),
|
|
3610
|
+
description: sanitizeUtf8(String(frontmatter.description || "")),
|
|
3605
3611
|
version: frontmatter.version ? String(frontmatter.version) : void 0,
|
|
3606
3612
|
license: frontmatter.license ? String(frontmatter.license) : void 0,
|
|
3607
3613
|
author: frontmatter.author ? String(frontmatter.author) : void 0,
|
|
@@ -3784,8 +3790,8 @@ function parseGenericInstructionFile(content, format, repoMeta) {
|
|
|
3784
3790
|
const derivedDescription = frontmatter.description || repoMeta.description || extractFirstParagraph(body) || `${FORMAT_LABELS[format]} from ${repoMeta.owner}/${repoMeta.name}`;
|
|
3785
3791
|
const inferredPlatform = pattern?.inferredPlatform || "claude";
|
|
3786
3792
|
const metadata = {
|
|
3787
|
-
name: derivedName,
|
|
3788
|
-
description: derivedDescription.slice(0, MAX_DESCRIPTION_LENGTH),
|
|
3793
|
+
name: sanitizeUtf8(derivedName),
|
|
3794
|
+
description: sanitizeUtf8(derivedDescription.slice(0, MAX_DESCRIPTION_LENGTH)),
|
|
3789
3795
|
version: frontmatter.version ? String(frontmatter.version) : void 0,
|
|
3790
3796
|
license: frontmatter.license ? String(frontmatter.license) : void 0,
|
|
3791
3797
|
author: frontmatter.author ? String(frontmatter.author) : repoMeta.owner,
|
|
@@ -3828,6 +3834,21 @@ function extractFirstParagraph(content) {
|
|
|
3828
3834
|
}
|
|
3829
3835
|
return null;
|
|
3830
3836
|
}
|
|
3837
|
+
var TAG_KEYS = [
|
|
3838
|
+
"RATIONALE",
|
|
3839
|
+
"USE-CASES",
|
|
3840
|
+
"AUDIENCE",
|
|
3841
|
+
"COMPLEMENTS",
|
|
3842
|
+
"SEO-EN",
|
|
3843
|
+
"BUNDLE-FIT",
|
|
3844
|
+
"FRAMEWORK-LOCK",
|
|
3845
|
+
"CONTRIBUTING-REPO",
|
|
3846
|
+
"MATURITY",
|
|
3847
|
+
"COMPLEXITY",
|
|
3848
|
+
"DEPENDENCIES",
|
|
3849
|
+
"PLATFORM"
|
|
3850
|
+
];
|
|
3851
|
+
var TAG_REGEX = new RegExp(`(${TAG_KEYS.join("|")}):\\s*`, "g");
|
|
3831
3852
|
|
|
3832
3853
|
// src/utils/api.ts
|
|
3833
3854
|
import https from "https";
|
|
@@ -4020,12 +4041,20 @@ async function fetchSkillContent(owner, repo, skillPath, branch = "main", source
|
|
|
4020
4041
|
pathsToTry.push(filename);
|
|
4021
4042
|
}
|
|
4022
4043
|
} else {
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4044
|
+
const skillName = skillPath ? skillPath.replace(/^(skills|\.claude\/skills|\.github\/skills)\//, "") : "";
|
|
4045
|
+
pathsToTry = [basePath];
|
|
4046
|
+
if (skillName) {
|
|
4047
|
+
const candidates = [
|
|
4048
|
+
`skills/${skillName}/${filename}`,
|
|
4049
|
+
`.claude/skills/${skillName}/${filename}`,
|
|
4050
|
+
`.github/skills/${skillName}/${filename}`
|
|
4051
|
+
];
|
|
4052
|
+
for (const candidate of candidates) {
|
|
4053
|
+
if (candidate !== basePath) {
|
|
4054
|
+
pathsToTry.push(candidate);
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4029
4058
|
}
|
|
4030
4059
|
for (const pathToTry of pathsToTry) {
|
|
4031
4060
|
try {
|
|
@@ -4047,18 +4076,45 @@ async function fetchSkillContent(owner, repo, skillPath, branch = "main", source
|
|
|
4047
4076
|
}
|
|
4048
4077
|
};
|
|
4049
4078
|
break;
|
|
4050
|
-
} catch
|
|
4051
|
-
|
|
4079
|
+
} catch {
|
|
4080
|
+
continue;
|
|
4052
4081
|
}
|
|
4053
4082
|
}
|
|
4054
4083
|
if (error.status === 404) {
|
|
4055
4084
|
continue;
|
|
4056
4085
|
}
|
|
4086
|
+
if (error.status === 403) {
|
|
4087
|
+
throw new Error(
|
|
4088
|
+
`GitHub API rate limit exceeded. Set GITHUB_TOKEN environment variable for higher rate limits.`
|
|
4089
|
+
);
|
|
4090
|
+
}
|
|
4057
4091
|
throw new Error(`Failed to fetch from GitHub: ${error.message}`);
|
|
4058
4092
|
}
|
|
4059
4093
|
}
|
|
4060
4094
|
if (!skillMdResponse) {
|
|
4061
|
-
|
|
4095
|
+
for (const pathToTry of pathsToTry) {
|
|
4096
|
+
try {
|
|
4097
|
+
const rawContent = await fetchRawFile(owner, repo, pathToTry, branch);
|
|
4098
|
+
skillMdResponse = {
|
|
4099
|
+
data: {
|
|
4100
|
+
content: Buffer.from(rawContent).toString("base64"),
|
|
4101
|
+
encoding: "base64"
|
|
4102
|
+
}
|
|
4103
|
+
};
|
|
4104
|
+
break;
|
|
4105
|
+
} catch {
|
|
4106
|
+
continue;
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
if (!skillMdResponse) {
|
|
4111
|
+
const triedPaths = pathsToTry.map((p) => ` - ${owner}/${repo}/${p}`).join("\n");
|
|
4112
|
+
throw new Error(
|
|
4113
|
+
`${filename} not found at ${owner}/${repo} (tried ${pathsToTry.length} paths):
|
|
4114
|
+
${triedPaths}
|
|
4115
|
+
|
|
4116
|
+
The skill may have been moved or deleted from the repository.`
|
|
4117
|
+
);
|
|
4062
4118
|
}
|
|
4063
4119
|
if (!("content" in skillMdResponse.data)) {
|
|
4064
4120
|
throw new Error(`${filename} not found`);
|
|
@@ -4263,7 +4319,8 @@ async function install(skillId, options2) {
|
|
|
4263
4319
|
try {
|
|
4264
4320
|
skillInfo = await getSkill(skillId);
|
|
4265
4321
|
if (skillInfo) {
|
|
4266
|
-
|
|
4322
|
+
const reviewBadge = skillInfo.aiScore && skillInfo.reviewStatus === "ai-reviewed" ? chalk.dim(` | AI: ${skillInfo.aiScore}/100`) : skillInfo.reviewStatus === "verified" ? chalk.green(` | AI: ${skillInfo.aiScore}/100 \u2713`) : "";
|
|
4323
|
+
spinner.succeed(`Found in registry: ${skillInfo.name}${reviewBadge}`);
|
|
4267
4324
|
spinner.start("Preparing installation...");
|
|
4268
4325
|
}
|
|
4269
4326
|
} catch (error) {
|
|
@@ -4309,11 +4366,22 @@ async function install(skillId, options2) {
|
|
|
4309
4366
|
spinner.text = "Downloading skill files...";
|
|
4310
4367
|
const cachedFiles = await getSkillFiles(skillInfo.id);
|
|
4311
4368
|
if (cachedFiles && cachedFiles.files.length > 0) {
|
|
4369
|
+
if (cachedFiles.isStale) {
|
|
4370
|
+
spinner.warn(chalk.yellow(
|
|
4371
|
+
"This skill may have been removed from its GitHub repository.\n Files were served from the SkillHub cache and may be outdated."
|
|
4372
|
+
));
|
|
4373
|
+
spinner.start("Installing from cached files...");
|
|
4374
|
+
}
|
|
4312
4375
|
if (cachedFiles.sourceFormat) {
|
|
4313
4376
|
sourceFormat = cachedFiles.sourceFormat;
|
|
4314
4377
|
}
|
|
4315
|
-
|
|
4316
|
-
|
|
4378
|
+
const converted = convertCachedFilesToSkillContent(cachedFiles, sourceFormat);
|
|
4379
|
+
if (converted.skillMd) {
|
|
4380
|
+
content = converted;
|
|
4381
|
+
spinner.text = cachedFiles.fromCache ? `Using cached files (${cachedFiles.files.length} files)` : `Downloaded ${cachedFiles.files.length} files via API`;
|
|
4382
|
+
} else {
|
|
4383
|
+
spinner.text = "API returned files but main instruction file missing, falling back...";
|
|
4384
|
+
}
|
|
4317
4385
|
}
|
|
4318
4386
|
} catch {
|
|
4319
4387
|
spinner.text = "API file fetch failed, falling back to GitHub...";
|
|
@@ -4570,6 +4638,12 @@ async function search(query, options2) {
|
|
|
4570
4638
|
` ${chalk2.yellow("\u2605")} ${skill.rating.toFixed(1)} ${chalk2.dim(`(${skill.ratingCount} ratings)`)}`
|
|
4571
4639
|
);
|
|
4572
4640
|
}
|
|
4641
|
+
if (skill.aiScore && skill.reviewStatus && skill.reviewStatus !== "unreviewed" && skill.reviewStatus !== "auto-scored") {
|
|
4642
|
+
const scoreColor = skill.aiScore >= 75 ? chalk2.green : skill.aiScore >= 50 ? chalk2.yellow : chalk2.dim;
|
|
4643
|
+
console.log(
|
|
4644
|
+
` ${chalk2.magenta("AI")} ${scoreColor(String(skill.aiScore))} ${chalk2.dim(`(${skill.reviewStatus})`)}`
|
|
4645
|
+
);
|
|
4646
|
+
}
|
|
4573
4647
|
console.log(chalk2.dim("\u2500".repeat(80)));
|
|
4574
4648
|
}
|
|
4575
4649
|
console.log();
|
package/package.json
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "skillhub",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "CLI tool for managing AI Agent skills - search, install, and update skills for Claude, Codex, Copilot, and more",
|
|
5
|
-
"author": "SkillHub Contributors",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "https://github.com/airano-ir/skillhub.git",
|
|
10
|
-
"directory": "apps/cli"
|
|
11
|
-
},
|
|
12
|
-
"homepage": "https://skills.palebluedot.live",
|
|
13
|
-
"type": "module",
|
|
14
|
-
"bin": {
|
|
15
|
-
"skillhub": "./dist/index.js"
|
|
16
|
-
},
|
|
17
|
-
"files": [
|
|
18
|
-
"dist"
|
|
19
|
-
],
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"@
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "skillhub",
|
|
3
|
+
"version": "0.2.6",
|
|
4
|
+
"description": "CLI tool for managing AI Agent skills - search, install, and update skills for Claude, Codex, Copilot, and more",
|
|
5
|
+
"author": "SkillHub Contributors",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/airano-ir/skillhub.git",
|
|
10
|
+
"directory": "apps/cli"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://skills.palebluedot.live",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"bin": {
|
|
15
|
+
"skillhub": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"dev": "tsx src/index.ts",
|
|
23
|
+
"start": "node dist/index.js",
|
|
24
|
+
"lint": "eslint src/",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test": "vitest",
|
|
27
|
+
"test:run": "vitest run"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@octokit/rest": "^20.0.2",
|
|
31
|
+
"chalk": "^5.3.0",
|
|
32
|
+
"commander": "^11.1.0",
|
|
33
|
+
"fs-extra": "^11.2.0",
|
|
34
|
+
"ora": "^8.0.1",
|
|
35
|
+
"prompts": "^2.4.2"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"skillhub-core": "workspace:*",
|
|
39
|
+
"@types/fs-extra": "^11.0.4",
|
|
40
|
+
"@types/node": "^20.10.0",
|
|
41
|
+
"@types/prompts": "^2.4.9",
|
|
42
|
+
"tsup": "^8.0.1",
|
|
43
|
+
"tsx": "^4.7.0",
|
|
44
|
+
"typescript": "^5.3.0",
|
|
45
|
+
"vitest": "^1.2.0"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18.0.0"
|
|
49
|
+
},
|
|
50
|
+
"keywords": [
|
|
51
|
+
"ai",
|
|
52
|
+
"agent",
|
|
53
|
+
"skills",
|
|
54
|
+
"cli",
|
|
55
|
+
"claude",
|
|
56
|
+
"codex",
|
|
57
|
+
"copilot",
|
|
58
|
+
"cursor",
|
|
59
|
+
"windsurf"
|
|
60
|
+
]
|
|
61
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 SkillHub Contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|