skills-manager 0.0.3 → 0.0.5
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/README.md +98 -78
- package/dist/index.mjs +90 -46
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# skills-manager
|
|
2
2
|
|
|
3
|
-
> 一个简单的 CLI
|
|
3
|
+
> 一个简单的 CLI 工具,用于管理技能包
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|

|
|
@@ -9,8 +9,12 @@
|
|
|
9
9
|
|
|
10
10
|
- ⚡️ **快速安装** - 使用 degit 从 GitHub 仓库快速克隆
|
|
11
11
|
- 🎯 **简单易用** - 清晰的 CLI 命令和友好的用户反馈
|
|
12
|
-
- 📦
|
|
12
|
+
- 📦 **全局/本地管理** - 技能包支持全局和本地安装模式
|
|
13
13
|
- 🔄 **依赖轻量** - 基于 degit,无需 git 配置
|
|
14
|
+
- 📁 **智能查找** - 自动识别仓库中的技能文件夹(优先查找 `skills/` 目录)
|
|
15
|
+
- 🚫 **自动清理** - 安装前自动清理旧版本,避免冲突
|
|
16
|
+
- 💾 **智能缓存** - 使用缓存加速重复安装
|
|
17
|
+
- 🎨 **彩色输出** - 使用 chalk 提供清晰的状态反馈
|
|
14
18
|
|
|
15
19
|
## 安装
|
|
16
20
|
|
|
@@ -26,46 +30,45 @@ pnpm add -g skills-manager
|
|
|
26
30
|
|
|
27
31
|
## 使用
|
|
28
32
|
|
|
29
|
-
### 查看可用技能
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
skills available
|
|
33
|
-
```
|
|
34
|
-
|
|
35
33
|
### 安装技能
|
|
36
34
|
|
|
37
35
|
```bash
|
|
38
|
-
|
|
36
|
+
# 本地安装
|
|
37
|
+
skills-manager add <owner>/<repo> <skill-name>
|
|
38
|
+
|
|
39
|
+
# 全局安装
|
|
40
|
+
skills-manager add <owner>/<repo> <skill-name> --global
|
|
39
41
|
```
|
|
40
42
|
|
|
41
43
|
例如:
|
|
42
44
|
|
|
43
45
|
```bash
|
|
44
|
-
|
|
45
|
-
skills add
|
|
46
|
-
skills add nuxt
|
|
46
|
+
# 本地安装
|
|
47
|
+
skills-manager add antfu/skills vue
|
|
47
48
|
```
|
|
48
49
|
|
|
49
50
|
### 列出已安装的技能
|
|
50
51
|
|
|
51
52
|
```bash
|
|
52
|
-
|
|
53
|
+
# 列出当前目录的技能
|
|
54
|
+
skills-manager list
|
|
53
55
|
# 或
|
|
54
|
-
skills ls
|
|
56
|
+
skills-manager ls
|
|
57
|
+
|
|
58
|
+
# 列出全局技能
|
|
59
|
+
skills-manager list --global
|
|
55
60
|
```
|
|
56
61
|
|
|
57
62
|
### 删除技能
|
|
58
63
|
|
|
59
64
|
```bash
|
|
60
|
-
|
|
65
|
+
# 删除当前目录的技能
|
|
66
|
+
skills-manager remove <skill-name>
|
|
61
67
|
# 或
|
|
62
|
-
skills rm <skill-name>
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### 查看帮助
|
|
68
|
+
skills-manager rm <skill-name>
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
skills --
|
|
70
|
+
# 删除全局技能
|
|
71
|
+
skills-manager remove <skill-name> --global
|
|
69
72
|
```
|
|
70
73
|
|
|
71
74
|
### 查看版本
|
|
@@ -74,82 +77,99 @@ skills --help
|
|
|
74
77
|
skills --version
|
|
75
78
|
```
|
|
76
79
|
|
|
77
|
-
## 可用技能
|
|
78
|
-
|
|
79
|
-
以下是当前支持的技能包:
|
|
80
|
-
|
|
81
|
-
| 技能名称 | 描述 |
|
|
82
|
-
|---------|------|
|
|
83
|
-
| `antfu` | Anthony Fu 的个人技能集 |
|
|
84
|
-
| `nuxt` | Nuxt.js 框架技能 |
|
|
85
|
-
| `pnpm` | pnpm 包管理器技能 |
|
|
86
|
-
| `slidev` | Slidev 演示文稿工具 |
|
|
87
|
-
| `tsdown` | Tsdown 构建工具 |
|
|
88
|
-
| `unocss` | UnoCSS 引擎技能 |
|
|
89
|
-
| `vite` | Vite 构建工具 |
|
|
90
|
-
| `vitest` | Vitest 测试框架 |
|
|
91
|
-
| `vue-best-practices` | Vue 最佳实践 |
|
|
92
|
-
| `vue-router-best-practices` | Vue Router 最佳实践 |
|
|
93
|
-
| `vue-testing-best-practices` | Vue 测试最佳实践 |
|
|
94
|
-
| `vue` | Vue.js 框架技能 |
|
|
95
|
-
| `vueuse-functions` | VueUse 函数集合 |
|
|
96
|
-
| `web-design-guidelines` | Web 设计指南 |
|
|
97
|
-
| `mcp-builder` | MCP 构建器 |
|
|
98
|
-
| `skill-creator` | 技能创建器 |
|
|
99
|
-
| `cloudflare` | Cloudflare 平台技能 |
|
|
100
|
-
| `wrangler` | Wrangler CLI 工具 |
|
|
101
|
-
| `agent-browser` | Agent 浏览器技能 |
|
|
102
|
-
| `javascript-testing-patterns` | JavaScript 测试模式 |
|
|
103
|
-
| `modern-javascript-patterns` | 现代 JavaScript 模式 |
|
|
104
|
-
| `typescript-advanced-types` | TypeScript 高级类型 |
|
|
105
|
-
|
|
106
|
-
## 技能存储位置
|
|
107
|
-
|
|
108
|
-
技能包默认安装到当前工作目录的 `.alma/skills/` 目录下。
|
|
109
|
-
|
|
110
80
|
## 工作原理
|
|
111
81
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
82
|
+
### 安装流程
|
|
83
|
+
|
|
84
|
+
1. **缓存下载** - 使用 degit 下载指定 GitHub 仓库到 `~/.cache/skills-manager`
|
|
85
|
+
2. **智能查找** - 自动查找技能文件夹:
|
|
86
|
+
- 优先查找 `skills/{skill-name}` 目录
|
|
87
|
+
- 如果未找到,递归搜索整个仓库
|
|
88
|
+
- 显示警告信息并继续查找
|
|
89
|
+
3. **准备安装** - 确定目标目录(全局或本地),创建必要的目录结构
|
|
90
|
+
4. **清理旧版** - 自动删除已存在的技能目录,避免冲突
|
|
91
|
+
5. **复制安装** - 将技能文件夹复制到最终位置
|
|
92
|
+
6. **完成反馈** - 显示安装成功的彩色信息
|
|
93
|
+
|
|
94
|
+
### 技能目录结构
|
|
95
|
+
|
|
96
|
+
技能仓库应该包含以下结构之一:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
# 推荐结构(优先匹配)
|
|
100
|
+
skills/
|
|
101
|
+
├── skill-name/
|
|
102
|
+
│ └── (技能文件)
|
|
103
|
+
└── ...
|
|
104
|
+
|
|
105
|
+
# 或者直接在根目录
|
|
106
|
+
skill-name/
|
|
107
|
+
└── (技能文件)
|
|
108
|
+
```
|
|
115
109
|
|
|
116
110
|
## 架构
|
|
117
111
|
|
|
118
112
|
```
|
|
119
113
|
src/
|
|
120
|
-
├── index.ts # CLI
|
|
114
|
+
├── index.ts # CLI 入口点(使用 cac 框架)
|
|
121
115
|
├── manager.ts # 技能管理器(基于 degit)
|
|
122
|
-
|
|
116
|
+
├── utils.ts # 工具函数(文件操作、颜色输出等)
|
|
117
|
+
└── types.ts # TypeScript 类型定义
|
|
123
118
|
```
|
|
124
119
|
|
|
125
|
-
##
|
|
120
|
+
## 缓存位置
|
|
126
121
|
|
|
127
|
-
|
|
128
|
-
# 克隆项目
|
|
129
|
-
git clone https://github.com/jiakun-zhao/skills-manager.git
|
|
130
|
-
cd skills-manager
|
|
122
|
+
技能包缓存存储在 `~/.cache/skills-manager/` 目录下,每个仓库根据名称创建单独的缓存文件夹。
|
|
131
123
|
|
|
132
|
-
|
|
133
|
-
pnpm install
|
|
124
|
+
### 技能安装位置
|
|
134
125
|
|
|
135
|
-
|
|
136
|
-
|
|
126
|
+
- **全局技能**: `~/.claude/skills/`
|
|
127
|
+
- **本地技能**: `./.claude/skills/`(相对于当前工作目录)
|
|
137
128
|
|
|
138
|
-
|
|
139
|
-
pnpm build
|
|
129
|
+
## 许可证
|
|
140
130
|
|
|
141
|
-
|
|
142
|
-
pnpm release
|
|
143
|
-
```
|
|
131
|
+
[MIT](LICENSE)
|
|
144
132
|
|
|
145
|
-
##
|
|
133
|
+
## 故障排除
|
|
146
134
|
|
|
147
|
-
|
|
148
|
-
- **[cac](https://github.com/cacjs/cac)** - CLI 命令行框架
|
|
135
|
+
### 常见问题
|
|
149
136
|
|
|
150
|
-
|
|
137
|
+
1. **技能未找到错误**
|
|
151
138
|
|
|
152
|
-
|
|
139
|
+
```
|
|
140
|
+
Error: Skill 'skill-name' not found in repo owner/repo
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
- 确保仓库中包含名为 `skill-name` 的目录
|
|
144
|
+
- 技能应该位于 `skills/skill-name` 或仓库的根目录
|
|
145
|
+
|
|
146
|
+
2. **权限错误**
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
Error: EACCES: permission denied
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- 检查对 `~/.claude/skills` 或当前目录的写权限
|
|
153
|
+
- 尝试使用 `sudo`(仅限全局安装)
|
|
154
|
+
|
|
155
|
+
3. **网络错误**
|
|
156
|
+
- 确保网络连接正常
|
|
157
|
+
- 检查 GitHub 仓库是否可以访问
|
|
158
|
+
|
|
159
|
+
### 调试模式
|
|
160
|
+
|
|
161
|
+
如果遇到问题,可以:
|
|
162
|
+
|
|
163
|
+
1. 检查缓存目录内容:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
ls ~/.cache/skills-manager/
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
2. 清理缓存后重试:
|
|
170
|
+
```bash
|
|
171
|
+
rm -rf ~/.cache/skills-manager/
|
|
172
|
+
```
|
|
153
173
|
|
|
154
174
|
## 作者
|
|
155
175
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,69 +1,113 @@
|
|
|
1
1
|
import { cac } from "cac";
|
|
2
|
+
import { cwd, exit } from "node:process";
|
|
2
3
|
import chalk from "chalk";
|
|
3
|
-
import
|
|
4
|
+
import degit from "degit";
|
|
5
|
+
import { access, copyFile, mkdir, readdir } from "node:fs/promises";
|
|
4
6
|
import { homedir } from "node:os";
|
|
5
|
-
import {
|
|
7
|
+
import { join } from "node:path";
|
|
6
8
|
import { rimraf } from "rimraf";
|
|
7
|
-
import { joinURL } from "ufo";
|
|
8
9
|
|
|
9
|
-
//#region src/
|
|
10
|
+
//#region src/utils.ts
|
|
10
11
|
const print = console.log;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
async function exists(path) {
|
|
13
|
+
try {
|
|
14
|
+
await access(path);
|
|
15
|
+
return true;
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
17
19
|
}
|
|
18
|
-
async function
|
|
19
|
-
await
|
|
20
|
-
await
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
async function copyDirectory(src, dest) {
|
|
21
|
+
await mkdir(dest, { recursive: true });
|
|
22
|
+
const entries = await readdir(src, { withFileTypes: true });
|
|
23
|
+
for (const entry of entries) {
|
|
24
|
+
const srcPath = join(src, entry.name);
|
|
25
|
+
const destPath = join(dest, entry.name);
|
|
26
|
+
if (entry.isDirectory()) await copyDirectory(srcPath, destPath);
|
|
27
|
+
else try {
|
|
28
|
+
await copyFile(srcPath, destPath);
|
|
29
|
+
} catch {}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function findSkillPath(repoPath, skillName) {
|
|
33
|
+
const skillDir = join(join(repoPath, "skills"), skillName);
|
|
34
|
+
if (await exists(skillDir)) return skillDir;
|
|
35
|
+
async function findDirectory(dir, target) {
|
|
36
|
+
try {
|
|
37
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
if (entry.isDirectory() && entry.name === target) return join(dir, target);
|
|
40
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
41
|
+
const result = await findDirectory(join(dir, entry.name), target);
|
|
42
|
+
if (result) return result;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} catch {}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const result = await findDirectory(repoPath, skillName);
|
|
49
|
+
if (!result) print(chalk.yellow(`Warning: Skill '${skillName}' not found in skills/ directory, searching entire repo...`));
|
|
50
|
+
return result;
|
|
23
51
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
await mkdir(dirname(file.local), { recursive: true });
|
|
35
|
-
await writeFile(file.local, data);
|
|
36
|
-
});
|
|
37
|
-
}));
|
|
38
|
-
await list();
|
|
39
|
-
print(chalk.green("+", name));
|
|
52
|
+
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/manager.ts
|
|
55
|
+
const GLOBAL_SKILLS_DIR = join(homedir(), ".claude", "skills");
|
|
56
|
+
const LOCAL_SKILLS_DIR = join(cwd(), ".claude", "skills");
|
|
57
|
+
const CACHE_DIR = join(homedir(), ".cache", "skills-manager");
|
|
58
|
+
async function list(options = {}) {
|
|
59
|
+
const skillsDir = options.global ? GLOBAL_SKILLS_DIR : LOCAL_SKILLS_DIR;
|
|
60
|
+
const type = options.global ? "global" : "local";
|
|
61
|
+
print(chalk.gray("→"), chalk.gray(skillsDir));
|
|
40
62
|
print();
|
|
63
|
+
try {
|
|
64
|
+
(await readdir(skillsDir, { withFileTypes: true })).forEach((it) => it.isDirectory() && print(chalk.gray("•"), it.name));
|
|
65
|
+
print();
|
|
66
|
+
} catch {
|
|
67
|
+
print(chalk.gray(`No ${type} skills found`));
|
|
68
|
+
}
|
|
41
69
|
}
|
|
42
|
-
function
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
70
|
+
async function remove(name, options = {}) {
|
|
71
|
+
const path = join(options.global ? GLOBAL_SKILLS_DIR : LOCAL_SKILLS_DIR, name);
|
|
72
|
+
if (!await exists(path)) throw new Error(`Skill '${name}' not found`);
|
|
73
|
+
await rimraf(path);
|
|
74
|
+
if (!options.global) {
|
|
75
|
+
if ((await readdir(LOCAL_SKILLS_DIR)).length === 0) await rimraf(join(LOCAL_SKILLS_DIR, ".."));
|
|
76
|
+
}
|
|
77
|
+
print(chalk.gray("-", options.global ? GLOBAL_SKILLS_DIR : LOCAL_SKILLS_DIR), chalk.red(name));
|
|
78
|
+
print();
|
|
50
79
|
}
|
|
51
|
-
async function
|
|
52
|
-
const
|
|
53
|
-
|
|
80
|
+
async function add(repo, skillName, options) {
|
|
81
|
+
const cachePath = join(CACHE_DIR, repo.replace(/\//g, "-"));
|
|
82
|
+
await mkdir(CACHE_DIR, { recursive: true });
|
|
83
|
+
print(chalk.gray("✲", "Downloading repo to cache..."));
|
|
84
|
+
await degit(repo, {
|
|
85
|
+
cache: true,
|
|
86
|
+
force: true
|
|
87
|
+
}).clone(cachePath);
|
|
88
|
+
print(chalk.green("✓", "Repo downloaded to cache"));
|
|
89
|
+
const skillPath = await findSkillPath(cachePath, skillName);
|
|
90
|
+
if (!skillPath) throw new Error(`Skill '${skillName}' not found in repo ${repo}`);
|
|
91
|
+
const skillsDir = options.global ? GLOBAL_SKILLS_DIR : LOCAL_SKILLS_DIR;
|
|
92
|
+
const targetDir = join(skillsDir, skillName);
|
|
93
|
+
await mkdir(skillsDir, { recursive: true });
|
|
94
|
+
if (await exists(targetDir)) await rimraf(targetDir);
|
|
95
|
+
await copyDirectory(skillPath, targetDir);
|
|
96
|
+
print(chalk.gray("→", options.global ? GLOBAL_SKILLS_DIR : LOCAL_SKILLS_DIR), chalk.green(skillName));
|
|
97
|
+
print();
|
|
54
98
|
}
|
|
55
99
|
|
|
56
100
|
//#endregion
|
|
57
101
|
//#region package.json
|
|
58
102
|
var name = "skills-manager";
|
|
59
|
-
var version = "0.0.
|
|
103
|
+
var version = "0.0.5";
|
|
60
104
|
|
|
61
105
|
//#endregion
|
|
62
106
|
//#region src/index.ts
|
|
63
107
|
const cli = cac(name);
|
|
64
|
-
cli.command("add <
|
|
65
|
-
cli.command("list", "List skills").alias("ls").action(list);
|
|
66
|
-
cli.command("remove <name>", "Remove skill").alias("rm").action(remove);
|
|
108
|
+
cli.command("add <repo> <skill-name>", "Add skill from GitHub repo").option("-g, --global", "Install globally").action((repo, skillName, options) => add(repo, skillName, options).catch(() => exit(1)));
|
|
109
|
+
cli.command("list", "List skills").alias("ls").option("-g, --global", "List global skills").action((options) => list(options).catch(() => exit(1)));
|
|
110
|
+
cli.command("remove <name>", "Remove skill").alias("rm").option("-g, --global", "Remove from global skills").action((name, options) => remove(name, options).catch(() => exit(1)));
|
|
67
111
|
cli.version(version);
|
|
68
112
|
cli.help();
|
|
69
113
|
cli.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skills-manager",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "",
|
|
5
5
|
"homepage": "https://github.com/jiakun-zhao/skills-manager#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -20,16 +20,16 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"cac": "^6.7.14",
|
|
22
22
|
"chalk": "^5.6.2",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
23
|
+
"degit": "^2.8.4",
|
|
24
|
+
"rimraf": "^6.1.2"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@jiakun-zhao/eslint-config": "^4.3.0",
|
|
28
28
|
"@types/degit": "^2.8.6",
|
|
29
|
-
"@types/node": "^25.2.
|
|
30
|
-
"bumpp": "^10.4.
|
|
29
|
+
"@types/node": "^25.2.1",
|
|
30
|
+
"bumpp": "^10.4.1",
|
|
31
31
|
"eslint": "^9.39.2",
|
|
32
|
-
"tsdown": "^0.20.
|
|
32
|
+
"tsdown": "^0.20.3",
|
|
33
33
|
"tsx": "^4.21.0",
|
|
34
34
|
"typescript": "^5.9.3"
|
|
35
35
|
},
|