skills-manager 0.0.1 → 0.0.3

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.mjs +56 -266
  2. package/package.json +12 -12
package/dist/index.mjs CHANGED
@@ -1,279 +1,69 @@
1
1
  import { cac } from "cac";
2
- import degit from "degit";
3
- import { existsSync } from "node:fs";
4
- import { mkdir, readdir, rm } from "node:fs/promises";
5
- import { join } from "node:path";
6
- import { cwd } from "node:process";
2
+ import chalk from "chalk";
3
+ import { mkdir, readdir, writeFile } from "node:fs/promises";
4
+ import { homedir } from "node:os";
5
+ import { dirname, join } from "node:path";
6
+ import { rimraf } from "rimraf";
7
+ import { joinURL } from "ufo";
7
8
 
8
- //#region src/skills.ts
9
- const skills = new Map([
10
- ["antfu", {
11
- path: "antfu/skills/skills/antfu",
12
- description: "Anthony Fu 的个人技能集"
13
- }],
14
- ["nuxt", {
15
- path: "antfu/skills/skills/nuxt",
16
- description: "Nuxt.js 框架技能"
17
- }],
18
- ["pnpm", {
19
- path: "antfu/skills/skills/pnpm",
20
- description: "pnpm 包管理器技能"
21
- }],
22
- ["slidev", {
23
- path: "antfu/skills/skills/slidev",
24
- description: "Slidev 演示文稿工具"
25
- }],
26
- ["tsdown", {
27
- path: "antfu/skills/skills/tsdown",
28
- description: "Tsdown 构建工具"
29
- }],
30
- ["unocss", {
31
- path: "antfu/skills/skills/unocss",
32
- description: "UnoCSS 引擎技能"
33
- }],
34
- ["vite", {
35
- path: "antfu/skills/skills/vite",
36
- description: "Vite 构建工具"
37
- }],
38
- ["vitest", {
39
- path: "antfu/skills/skills/vitest",
40
- description: "Vitest 测试框架"
41
- }],
42
- ["vue-best-practices", {
43
- path: "antfu/skills/skills/vue-best-practices",
44
- description: "Vue 最佳实践"
45
- }],
46
- ["vue-router-best-practices", {
47
- path: "antfu/skills/skills/vue-router-best-practices",
48
- description: "Vue Router 最佳实践"
49
- }],
50
- ["vue-testing-best-practices", {
51
- path: "antfu/skills/skills/vue-testing-best-practices",
52
- description: "Vue 测试最佳实践"
53
- }],
54
- ["vue", {
55
- path: "antfu/skills/skills/vue",
56
- description: "Vue.js 框架技能"
57
- }],
58
- ["vueuse-functions", {
59
- path: "antfu/skills/skills/vueuse-functions",
60
- description: "VueUse 函数集合"
61
- }],
62
- ["web-design-guidelines", {
63
- path: "antfu/skills/skills/web-design-guidelines",
64
- description: "Web 设计指南"
65
- }],
66
- ["mcp-builder", {
67
- path: "anthropics/skills/skills/mcp-builder",
68
- description: "MCP 构建器"
69
- }],
70
- ["skill-creator", {
71
- path: "anthropics/skills/skills/skill-creator",
72
- description: "技能创建器"
73
- }],
74
- ["cloudflare", {
75
- path: "cloudflare/skills/skills/cloudflare",
76
- description: "Cloudflare 平台技能"
77
- }],
78
- ["wrangler", {
79
- path: "cloudflare/skills/skills/wrangler",
80
- description: "Wrangler CLI 工具"
81
- }],
82
- ["agent-browser", {
83
- path: "vercel-labs/agent-browser/skills/agent-browser",
84
- description: "Agent 浏览器技能"
85
- }],
86
- ["javascript-testing-patterns", {
87
- path: "wshobson/agents/plugins/javascript-typescript/skills/javascript-testing-patterns",
88
- description: "JavaScript 测试模式"
89
- }],
90
- ["modern-javascript-patterns", {
91
- path: "wshobson/agents/plugins/javascript-typescript/skills/modern-javascript-patterns",
92
- description: "现代 JavaScript 模式"
93
- }],
94
- ["typescript-advanced-types", {
95
- path: "wshobson/agents/plugins/javascript-typescript/skills/typescript-advanced-types",
96
- description: "TypeScript 高级类型"
97
- }]
98
- ]);
99
-
100
- //#endregion
101
- //#region src/utils.ts
102
- /**
103
- * 带颜色的控制台输出工具
104
- */
105
- const colors = {
106
- reset: "\x1B[0m",
107
- bright: "\x1B[1m",
108
- dim: "\x1B[2m",
109
- red: "\x1B[31m",
110
- green: "\x1B[32m",
111
- yellow: "\x1B[33m",
112
- blue: "\x1B[34m",
113
- magenta: "\x1B[35m",
114
- cyan: "\x1B[36m",
115
- white: "\x1B[37m"
116
- };
117
- const logger = {
118
- success(message) {
119
- console.log(`${colors.green}✅ ${message}${colors.reset}`);
120
- },
121
- error(message) {
122
- console.error(`${colors.red}❌ ${message}${colors.reset}`);
123
- },
124
- warn(message) {
125
- console.warn(`${colors.yellow}⚠️ ${message}${colors.reset}`);
126
- },
127
- info(message) {
128
- console.log(`${colors.blue}ℹ️ ${message}${colors.reset}`);
129
- },
130
- text(message) {
131
- console.log(`${colors.bright}${message}${colors.reset}`);
132
- },
133
- dim(message) {
134
- console.log(`${colors.dim}${message}${colors.reset}`);
135
- },
136
- item(message) {
137
- console.log(` ${colors.cyan}•${colors.reset} ${message}`);
138
- },
139
- title(message) {
140
- console.log(`\n${colors.bright}${colors.cyan}${message}${colors.reset}`);
141
- }
142
- };
143
-
144
- //#endregion
145
9
  //#region src/manager.ts
146
- const SKILLS_DIR = join(cwd(), ".alma", "skills");
147
- var SkillManager = class {
148
- /**
149
- * 验证技能是否存在
150
- */
151
- validate(skillName) {
152
- return skills.has(skillName);
153
- }
154
- /**
155
- * 获取技能安装路径
156
- */
157
- getSkillPath(skillName) {
158
- return join(SKILLS_DIR, skillName);
159
- }
160
- /**
161
- * 检查技能是否已安装
162
- */
163
- async isInstalled(skillName) {
164
- return existsSync(this.getSkillPath(skillName));
165
- }
166
- /**
167
- * 添加技能
168
- */
169
- async add(skillName) {
170
- if (!this.validate(skillName)) throw new Error(`技能 "${skillName}" 不存在`);
171
- const skill = skills.get(skillName);
172
- logger.title(`正在安装技能: ${skillName}`);
173
- if (skill.description) logger.dim(`描述: ${skill.description}`);
174
- logger.dim(`来源: ${skill.path}`);
175
- const targetDir = this.getSkillPath(skillName);
176
- if (existsSync(targetDir)) {
177
- logger.warn(`技能 "${skillName}" 已经安装`);
178
- return;
179
- }
180
- await mkdir(SKILLS_DIR, { recursive: true });
181
- const emitter = degit(skill.path, {
182
- force: true,
183
- verbose: false
184
- });
185
- try {
186
- await emitter.clone(targetDir);
187
- logger.success(`技能 "${skillName}" 安装成功!`);
188
- } catch (error) {
189
- if (existsSync(targetDir)) await rm(targetDir, {
190
- recursive: true,
191
- force: true
192
- });
193
- throw error;
194
- }
195
- }
196
- /**
197
- * 删除技能
198
- */
199
- async remove(skillName) {
200
- const skillPath = this.getSkillPath(skillName);
201
- if (!existsSync(skillPath)) throw new Error(`技能 "${skillName}" 未安装`);
202
- logger.title(`正在删除技能: ${skillName}`);
203
- await rm(skillPath, {
204
- recursive: true,
205
- force: true
10
+ const print = console.log;
11
+ const CLAUDE_SKILLS_DIR = join(homedir(), ".claude", "skills");
12
+ async function list() {
13
+ print(chalk.gray("→"), chalk.gray(CLAUDE_SKILLS_DIR));
14
+ print();
15
+ (await readdir(CLAUDE_SKILLS_DIR, { withFileTypes: true })).forEach((it) => it.isDirectory() && print(chalk.gray("•"), it.name));
16
+ print();
17
+ }
18
+ async function remove(name) {
19
+ await rimraf(join(CLAUDE_SKILLS_DIR, name));
20
+ await list();
21
+ print(chalk.red("-", name));
22
+ print();
23
+ }
24
+ async function add(key, options) {
25
+ const { owner, repo, name, dir } = format(key);
26
+ const files = (await fetchUnGH(owner, repo, options.branch)).filter((file) => file.path.startsWith(dir)).map((file) => ({
27
+ remote: joinURL("https://cdn.jsdelivr.net/gh", owner, repo, file.path),
28
+ local: join(CLAUDE_SKILLS_DIR, name, file.path.slice(dir.length))
29
+ }));
30
+ await rimraf(join(CLAUDE_SKILLS_DIR, name));
31
+ await Promise.all(files.map(async (file) => {
32
+ await fetch(file.remote).then(async (res) => {
33
+ const data = await res.text();
34
+ await mkdir(dirname(file.local), { recursive: true });
35
+ await writeFile(file.local, data);
206
36
  });
207
- logger.success(`技能 "${skillName}" 删除成功!`);
208
- }
209
- /**
210
- * 列出已安装的技能
211
- */
212
- async list() {
213
- if (!existsSync(SKILLS_DIR)) return [];
214
- return (await readdir(SKILLS_DIR, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
215
- }
216
- /**
217
- * 列出所有可用的技能
218
- */
219
- listAvailable() {
220
- return Array.from(skills.entries()).map(([name, skill]) => ({
221
- name,
222
- description: skill.description
223
- }));
224
- }
225
- };
226
- const manager = new SkillManager();
37
+ }));
38
+ await list();
39
+ print(chalk.green("+", name));
40
+ print();
41
+ }
42
+ function format(key) {
43
+ const [owner, repo, ...rest] = key.split("/");
44
+ return {
45
+ owner,
46
+ repo,
47
+ name: rest.at(-1),
48
+ dir: rest.join("/")
49
+ };
50
+ }
51
+ async function fetchUnGH(owner, repo, branch) {
52
+ const url = joinURL("https://api.zhaojiakun.com", "ungh", "repos", owner, repo, "files", branch);
53
+ return (await fetch(url).then((res) => res.json())).files;
54
+ }
227
55
 
228
56
  //#endregion
229
57
  //#region package.json
230
- var version = "0.0.1";
58
+ var name = "skills-manager";
59
+ var version = "0.0.3";
231
60
 
232
61
  //#endregion
233
62
  //#region src/index.ts
234
- const cli = cac("skills");
235
- cli.command("add <skill>", "Add a skill").action(async (skill) => {
236
- try {
237
- await manager.add(skill);
238
- } catch (error) {
239
- logger.error(error instanceof Error ? error.message : String(error));
240
- process.exit(1);
241
- }
242
- });
243
- cli.command("remove <skill>", "Remove a skill").alias("rm").action(async (skill) => {
244
- try {
245
- await manager.remove(skill);
246
- } catch (error) {
247
- logger.error(error instanceof Error ? error.message : String(error));
248
- process.exit(1);
249
- }
250
- });
251
- cli.command("list", "List installed skills").alias("ls").action(async () => {
252
- try {
253
- const installed = await manager.list();
254
- if (installed.length === 0) logger.warn("未安装任何技能");
255
- else {
256
- logger.title(`已安装的技能 (${installed.length}):`);
257
- installed.forEach((skill) => logger.item(skill));
258
- }
259
- } catch (error) {
260
- logger.error(error instanceof Error ? error.message : String(error));
261
- process.exit(1);
262
- }
263
- });
264
- cli.command("available", "List all available skills").alias("avail").action(() => {
265
- try {
266
- const available = manager.listAvailable();
267
- logger.title(`可用的技能 (${available.length}):`);
268
- available.forEach((skill) => {
269
- if (skill.description) logger.item(`${skill.name} - ${skill.description}`);
270
- else logger.item(skill.name);
271
- });
272
- } catch (error) {
273
- logger.error(error instanceof Error ? error.message : String(error));
274
- process.exit(1);
275
- }
276
- });
63
+ const cli = cac(name);
64
+ cli.command("add <key>", "Add skill").option("-b, --branch", "Repo branch", { default: "main" }).action(add);
65
+ cli.command("list", "List skills").alias("ls").action(list);
66
+ cli.command("remove <name>", "Remove skill").alias("rm").action(remove);
277
67
  cli.version(version);
278
68
  cli.help();
279
69
  cli.parse();
package/package.json CHANGED
@@ -1,30 +1,30 @@
1
1
  {
2
2
  "name": "skills-manager",
3
- "type": "module",
4
- "version": "0.0.1",
3
+ "version": "0.0.3",
5
4
  "description": "",
6
- "author": "Jiakun Zhao <hi@zhaojiakun.com>",
7
- "license": "MIT",
8
5
  "homepage": "https://github.com/jiakun-zhao/skills-manager#readme",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/jiakun-zhao/skills-manager.git"
12
- },
13
6
  "bugs": {
14
7
  "url": "https://github.com/jiakun-zhao/skills-manager/issues"
15
8
  },
16
- "bin": {
17
- "skills": "./dist/index.mjs"
9
+ "license": "MIT",
10
+ "author": "Jiakun Zhao <hi@zhaojiakun.com>",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/jiakun-zhao/skills-manager.git"
18
14
  },
15
+ "bin": "./dist/index.mjs",
19
16
  "files": [
20
17
  "dist"
21
18
  ],
19
+ "type": "module",
22
20
  "dependencies": {
23
21
  "cac": "^6.7.14",
24
- "degit": "^2.8.4"
22
+ "chalk": "^5.6.2",
23
+ "rimraf": "^6.1.2",
24
+ "ufo": "^1.6.3"
25
25
  },
26
26
  "devDependencies": {
27
- "@jiakun-zhao/eslint-config": "^4.2.0",
27
+ "@jiakun-zhao/eslint-config": "^4.3.0",
28
28
  "@types/degit": "^2.8.6",
29
29
  "@types/node": "^25.2.0",
30
30
  "bumpp": "^10.4.0",