@spardutti/claude-skills 1.5.0 → 1.7.0
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/bin/cli.mjs +11 -4
- package/lib/github.mjs +6 -4
- package/lib/install.mjs +8 -1
- package/lib/prompt.mjs +45 -5
- package/package.json +3 -2
package/bin/cli.mjs
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { confirm } from "@inquirer/prompts";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
4
8
|
import { fetchSkills } from "../lib/github.mjs";
|
|
5
9
|
import { promptSkillSelection } from "../lib/prompt.mjs";
|
|
6
10
|
import { installSkills } from "../lib/install.mjs";
|
|
7
11
|
import { setupHook } from "../lib/setup-hook.mjs";
|
|
8
12
|
import { setupClaudeMd } from "../lib/setup-claude-md.mjs";
|
|
9
13
|
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
16
|
+
|
|
10
17
|
async function main() {
|
|
11
|
-
console.log(
|
|
18
|
+
console.log(`\n ${chalk.bold.cyan("Claude Skills Installer")} ${chalk.dim(`v${pkg.version}`)}\n`);
|
|
12
19
|
|
|
13
|
-
console.log(" Fetching available skills...\n");
|
|
20
|
+
console.log(chalk.dim(" Fetching available skills...\n"));
|
|
14
21
|
const skills = await fetchSkills();
|
|
15
22
|
|
|
16
23
|
if (skills.length === 0) {
|
|
@@ -40,7 +47,7 @@ async function main() {
|
|
|
40
47
|
await setupClaudeMd();
|
|
41
48
|
}
|
|
42
49
|
|
|
43
|
-
console.log(`\n
|
|
50
|
+
console.log(`\n ${chalk.green("✔")} ${chalk.bold(`${selected.length} skill(s) installed successfully.`)}\n`);
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
main().catch((err) => {
|
|
@@ -48,6 +55,6 @@ main().catch((err) => {
|
|
|
48
55
|
console.log("\n Cancelled.\n");
|
|
49
56
|
process.exit(0);
|
|
50
57
|
}
|
|
51
|
-
console.error(`\n Error: ${err.message}\n`);
|
|
58
|
+
console.error(`\n ${chalk.red("Error:")} ${err.message}\n`);
|
|
52
59
|
process.exit(1);
|
|
53
60
|
});
|
package/lib/github.mjs
CHANGED
|
@@ -59,9 +59,9 @@ export async function fetchSkills() {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
const content = await r.text();
|
|
62
|
-
const { name, description } = parseFrontmatter(content, dir.name);
|
|
62
|
+
const { name, description, category } = parseFrontmatter(content, dir.name);
|
|
63
63
|
|
|
64
|
-
return { dirName: dir.name, name, description, content };
|
|
64
|
+
return { dirName: dir.name, name, description, category, content };
|
|
65
65
|
} catch {
|
|
66
66
|
console.warn(` Warning: Failed to fetch ${dir.name}, skipping`);
|
|
67
67
|
return null;
|
|
@@ -75,7 +75,7 @@ export async function fetchSkills() {
|
|
|
75
75
|
function parseFrontmatter(content, fallbackName) {
|
|
76
76
|
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
77
77
|
if (!match) {
|
|
78
|
-
return { name: fallbackName, description: "" };
|
|
78
|
+
return { name: fallbackName, description: "", category: "General" };
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const block = match[1];
|
|
@@ -83,6 +83,8 @@ function parseFrontmatter(content, fallbackName) {
|
|
|
83
83
|
block.match(/^name:\s*(.+)$/m)?.[1]?.trim() || fallbackName;
|
|
84
84
|
const description =
|
|
85
85
|
block.match(/^description:\s*(.+)$/m)?.[1]?.trim() || "";
|
|
86
|
+
const category =
|
|
87
|
+
block.match(/^category:\s*(.+)$/m)?.[1]?.trim() || "General";
|
|
86
88
|
|
|
87
|
-
return { name, description };
|
|
89
|
+
return { name, description, category };
|
|
88
90
|
}
|
package/lib/install.mjs
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
|
|
5
|
+
function humanName(skill) {
|
|
6
|
+
return skill.name
|
|
7
|
+
.replace(/-/g, " ")
|
|
8
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
9
|
+
}
|
|
3
10
|
|
|
4
11
|
export async function installSkills(skills, targetDir = process.cwd()) {
|
|
5
12
|
const baseDir = join(targetDir, ".claude", "skills");
|
|
@@ -8,6 +15,6 @@ export async function installSkills(skills, targetDir = process.cwd()) {
|
|
|
8
15
|
const skillDir = join(baseDir, skill.dirName);
|
|
9
16
|
await mkdir(skillDir, { recursive: true });
|
|
10
17
|
await writeFile(join(skillDir, "SKILL.md"), skill.content);
|
|
11
|
-
console.log(`
|
|
18
|
+
console.log(` ${chalk.green("✔")} ${chalk.bold(humanName(skill))} ${chalk.dim(`→ .claude/skills/${skill.dirName}/`)}`);
|
|
12
19
|
}
|
|
13
20
|
}
|
package/lib/prompt.mjs
CHANGED
|
@@ -1,12 +1,52 @@
|
|
|
1
|
-
import { checkbox } from "@inquirer/prompts";
|
|
1
|
+
import { checkbox, Separator } from "@inquirer/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
|
|
4
|
+
const CATEGORY_ORDER = ["Frontend", "TypeScript", "Backend", "Quality", "General"];
|
|
5
|
+
|
|
6
|
+
function humanName(skill) {
|
|
7
|
+
return skill.name
|
|
8
|
+
.replace(/-/g, " ")
|
|
9
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function stripQuotes(str) {
|
|
13
|
+
return str.replace(/^["']|["']$/g, "");
|
|
14
|
+
}
|
|
2
15
|
|
|
3
16
|
export async function promptSkillSelection(skills) {
|
|
17
|
+
// Group skills by category preserving order
|
|
18
|
+
const grouped = new Map();
|
|
19
|
+
for (const cat of CATEGORY_ORDER) {
|
|
20
|
+
const items = skills.filter((s) => s.category === cat);
|
|
21
|
+
if (items.length > 0) grouped.set(cat, items);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Build choices with separators
|
|
25
|
+
const choices = [];
|
|
26
|
+
for (const [category, items] of grouped) {
|
|
27
|
+
choices.push(new Separator(`── ${category} ${"─".repeat(35 - category.length)}`));
|
|
28
|
+
for (const skill of items) {
|
|
29
|
+
choices.push({
|
|
30
|
+
name: chalk.bold(humanName(skill)),
|
|
31
|
+
value: skill,
|
|
32
|
+
description: chalk.dim(stripQuotes(skill.description)),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const theme = {
|
|
38
|
+
icon: { cursor: ">" },
|
|
39
|
+
style: {
|
|
40
|
+
highlight: (text) => chalk.cyan(text),
|
|
41
|
+
renderSelectedChoices: (selected) =>
|
|
42
|
+
selected.map((s) => chalk.cyan(humanName(s))).join(", "),
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
4
46
|
const selected = await checkbox({
|
|
5
47
|
message: "Select skills to install:",
|
|
6
|
-
choices
|
|
7
|
-
|
|
8
|
-
value: skill,
|
|
9
|
-
})),
|
|
48
|
+
choices,
|
|
49
|
+
theme,
|
|
10
50
|
});
|
|
11
51
|
|
|
12
52
|
return selected;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spardutti/claude-skills",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "CLI to install Claude Code skills from the claude-skills collection",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"author": "spardutti",
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@inquirer/prompts": "^7.0.0"
|
|
25
|
+
"@inquirer/prompts": "^7.0.0",
|
|
26
|
+
"chalk": "^5.0.0"
|
|
26
27
|
}
|
|
27
28
|
}
|