skillsmgr 0.1.0 → 0.3.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/README.md +30 -32
- package/README.zh-CN.md +153 -0
- package/dist/index.js +231 -137
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Unified skills manager for AI coding tools. Manage skills in `~/.skills-manager/` and deploy them to multiple AI tools.
|
|
4
4
|
|
|
5
|
+
[中文文档](./README.zh-CN.md)
|
|
6
|
+
|
|
5
7
|
## Supported Tools
|
|
6
8
|
|
|
7
9
|
| Tool | Skills Directory | Mode-Specific |
|
|
@@ -12,77 +14,73 @@ Unified skills manager for AI coding tools. Manage skills in `~/.skills-manager/
|
|
|
12
14
|
| Cline | `.cline/skills/` | No |
|
|
13
15
|
| Roo Code | `.roo/skills/` | Yes |
|
|
14
16
|
| Kilo Code | `.kilocode/skills/` | Yes |
|
|
17
|
+
| OpenCode | `.opencode/skills/` | No |
|
|
18
|
+
| Trae | `.trae/skills/` | No |
|
|
15
19
|
| Antigravity | `.agent/skills/` | No |
|
|
16
20
|
|
|
17
|
-
## Installation
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install -g skillsmgr
|
|
21
|
-
```
|
|
22
|
-
|
|
23
21
|
## Quick Start
|
|
24
22
|
|
|
25
23
|
```bash
|
|
26
24
|
# Initialize skills manager
|
|
27
|
-
skillsmgr setup
|
|
25
|
+
npx skillsmgr setup
|
|
28
26
|
|
|
29
27
|
# Install official Anthropic skills
|
|
30
|
-
skillsmgr install anthropic
|
|
28
|
+
npx skillsmgr install anthropic
|
|
31
29
|
|
|
32
30
|
# Deploy skills to your project
|
|
33
31
|
cd your-project
|
|
34
|
-
skillsmgr init
|
|
32
|
+
npx skillsmgr init
|
|
35
33
|
```
|
|
36
34
|
|
|
37
35
|
## Commands
|
|
38
36
|
|
|
39
|
-
### `skillsmgr setup`
|
|
37
|
+
### `npx skillsmgr setup`
|
|
40
38
|
|
|
41
39
|
Initialize `~/.skills-manager/` directory structure with example skill.
|
|
42
40
|
|
|
43
41
|
```bash
|
|
44
|
-
skillsmgr setup
|
|
42
|
+
npx skillsmgr setup
|
|
45
43
|
```
|
|
46
44
|
|
|
47
|
-
### `skillsmgr install <source>`
|
|
45
|
+
### `npx skillsmgr install <source>`
|
|
48
46
|
|
|
49
47
|
Download skills from a repository.
|
|
50
48
|
|
|
51
49
|
```bash
|
|
52
50
|
# Install official Anthropic skills
|
|
53
|
-
skillsmgr install anthropic
|
|
51
|
+
npx skillsmgr install anthropic
|
|
54
52
|
|
|
55
53
|
# Install from any GitHub repository
|
|
56
|
-
skillsmgr install https://github.com/user/skills-repo
|
|
54
|
+
npx skillsmgr install https://github.com/user/skills-repo
|
|
57
55
|
|
|
58
56
|
# Install specific skill
|
|
59
|
-
skillsmgr install https://github.com/anthropics/skills/tree/main/skills/code-review
|
|
57
|
+
npx skillsmgr install https://github.com/anthropics/skills/tree/main/skills/code-review
|
|
60
58
|
|
|
61
59
|
# Install all skills without prompting
|
|
62
|
-
skillsmgr install anthropic --all
|
|
60
|
+
npx skillsmgr install anthropic --all
|
|
63
61
|
|
|
64
62
|
# Install to custom/ instead of community/
|
|
65
|
-
skillsmgr install https://github.com/user/repo --custom
|
|
63
|
+
npx skillsmgr install https://github.com/user/repo --custom
|
|
66
64
|
```
|
|
67
65
|
|
|
68
|
-
### `skillsmgr list`
|
|
66
|
+
### `npx skillsmgr list`
|
|
69
67
|
|
|
70
68
|
List available skills.
|
|
71
69
|
|
|
72
70
|
```bash
|
|
73
71
|
# List all available skills
|
|
74
|
-
skillsmgr list
|
|
72
|
+
npx skillsmgr list
|
|
75
73
|
|
|
76
74
|
# List deployed skills in current project
|
|
77
|
-
skillsmgr list --deployed
|
|
75
|
+
npx skillsmgr list --deployed
|
|
78
76
|
```
|
|
79
77
|
|
|
80
|
-
### `skillsmgr init`
|
|
78
|
+
### `npx skillsmgr init`
|
|
81
79
|
|
|
82
80
|
Interactive deployment of skills to current project.
|
|
83
81
|
|
|
84
82
|
```bash
|
|
85
|
-
skillsmgr init
|
|
83
|
+
npx skillsmgr init
|
|
86
84
|
```
|
|
87
85
|
|
|
88
86
|
Features:
|
|
@@ -91,39 +89,39 @@ Features:
|
|
|
91
89
|
- Choose skills to deploy with search filter
|
|
92
90
|
- Incremental updates (add/remove skills)
|
|
93
91
|
|
|
94
|
-
### `skillsmgr add <skill>`
|
|
92
|
+
### `npx skillsmgr add <skill>`
|
|
95
93
|
|
|
96
94
|
Quick add a skill to project.
|
|
97
95
|
|
|
98
96
|
```bash
|
|
99
97
|
# Add to all configured tools
|
|
100
|
-
skillsmgr add code-review
|
|
98
|
+
npx skillsmgr add code-review
|
|
101
99
|
|
|
102
100
|
# Add to specific tool
|
|
103
|
-
skillsmgr add code-review --tool claude-code
|
|
101
|
+
npx skillsmgr add code-review --tool claude-code
|
|
104
102
|
|
|
105
103
|
# Use copy mode instead of symlink
|
|
106
|
-
skillsmgr add code-review --copy
|
|
104
|
+
npx skillsmgr add code-review --copy
|
|
107
105
|
```
|
|
108
106
|
|
|
109
|
-
### `skillsmgr remove <skill>`
|
|
107
|
+
### `npx skillsmgr remove <skill>`
|
|
110
108
|
|
|
111
109
|
Remove a skill from project.
|
|
112
110
|
|
|
113
111
|
```bash
|
|
114
112
|
# Remove from all tools
|
|
115
|
-
skillsmgr remove code-review
|
|
113
|
+
npx skillsmgr remove code-review
|
|
116
114
|
|
|
117
115
|
# Remove from specific tool
|
|
118
|
-
skillsmgr remove code-review --tool claude-code
|
|
116
|
+
npx skillsmgr remove code-review --tool claude-code
|
|
119
117
|
```
|
|
120
118
|
|
|
121
|
-
### `skillsmgr sync`
|
|
119
|
+
### `npx skillsmgr sync`
|
|
122
120
|
|
|
123
121
|
Sync and verify deployed skills.
|
|
124
122
|
|
|
125
123
|
```bash
|
|
126
|
-
skillsmgr sync
|
|
124
|
+
npx skillsmgr sync
|
|
127
125
|
```
|
|
128
126
|
|
|
129
127
|
## Directory Structure
|
|
@@ -144,7 +142,7 @@ skillsmgr sync
|
|
|
144
142
|
## Features
|
|
145
143
|
|
|
146
144
|
- **Unified Management**: Manage all skills in one place
|
|
147
|
-
- **Multi-tool Support**: Deploy to
|
|
145
|
+
- **Multi-tool Support**: Deploy to 9 different AI tools
|
|
148
146
|
- **Symlink by Default**: Changes sync automatically
|
|
149
147
|
- **Search Filter**: Quick search for large skill repositories
|
|
150
148
|
- **Progress Indicators**: Visual feedback during downloads
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# skillsmgr
|
|
2
|
+
|
|
3
|
+
AI 编码工具的统一 Skills 管理器。在 `~/.skills-manager/` 中管理 skills,并部署到多个 AI 工具。
|
|
4
|
+
|
|
5
|
+
[English](./README.md)
|
|
6
|
+
|
|
7
|
+
## 支持的工具
|
|
8
|
+
|
|
9
|
+
| 工具 | Skills 目录 | 支持模式特定 |
|
|
10
|
+
|------|------------|-------------|
|
|
11
|
+
| Claude Code | `.claude/skills/` | 否 |
|
|
12
|
+
| Cursor | `.cursor/skills/` | 否 |
|
|
13
|
+
| Windsurf | `.windsurf/skills/` | 否 |
|
|
14
|
+
| Cline | `.cline/skills/` | 否 |
|
|
15
|
+
| Roo Code | `.roo/skills/` | 是 |
|
|
16
|
+
| Kilo Code | `.kilocode/skills/` | 是 |
|
|
17
|
+
| OpenCode | `.opencode/skills/` | 否 |
|
|
18
|
+
| Trae | `.trae/skills/` | 否 |
|
|
19
|
+
| Antigravity | `.agent/skills/` | 否 |
|
|
20
|
+
|
|
21
|
+
## 快速开始
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# 初始化 skills 管理器
|
|
25
|
+
npx skillsmgr setup
|
|
26
|
+
|
|
27
|
+
# 安装官方 Anthropic skills
|
|
28
|
+
npx skillsmgr install anthropic
|
|
29
|
+
|
|
30
|
+
# 部署 skills 到你的项目
|
|
31
|
+
cd your-project
|
|
32
|
+
npx skillsmgr init
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 命令
|
|
36
|
+
|
|
37
|
+
### `npx skillsmgr setup`
|
|
38
|
+
|
|
39
|
+
初始化 `~/.skills-manager/` 目录结构,包含示例 skill。
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx skillsmgr setup
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### `npx skillsmgr install <source>`
|
|
46
|
+
|
|
47
|
+
从仓库下载 skills。
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# 安装官方 Anthropic skills
|
|
51
|
+
npx skillsmgr install anthropic
|
|
52
|
+
|
|
53
|
+
# 从任意 GitHub 仓库安装
|
|
54
|
+
npx skillsmgr install https://github.com/user/skills-repo
|
|
55
|
+
|
|
56
|
+
# 安装特定 skill
|
|
57
|
+
npx skillsmgr install https://github.com/anthropics/skills/tree/main/skills/code-review
|
|
58
|
+
|
|
59
|
+
# 安装所有 skills(无需确认)
|
|
60
|
+
npx skillsmgr install anthropic --all
|
|
61
|
+
|
|
62
|
+
# 安装到 custom/ 而非 community/
|
|
63
|
+
npx skillsmgr install https://github.com/user/repo --custom
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### `npx skillsmgr list`
|
|
67
|
+
|
|
68
|
+
列出可用的 skills。
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# 列出所有可用 skills
|
|
72
|
+
npx skillsmgr list
|
|
73
|
+
|
|
74
|
+
# 列出当前项目已部署的 skills
|
|
75
|
+
npx skillsmgr list --deployed
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### `npx skillsmgr init`
|
|
79
|
+
|
|
80
|
+
交互式部署 skills 到当前项目。
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx skillsmgr init
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
功能:
|
|
87
|
+
- 选择目标工具(Claude Code、Cursor 等)
|
|
88
|
+
- 为 Roo Code / Kilo Code 选择模式
|
|
89
|
+
- 通过搜索过滤选择要部署的 skills
|
|
90
|
+
- 增量更新(添加/移除 skills)
|
|
91
|
+
|
|
92
|
+
### `npx skillsmgr add <skill>`
|
|
93
|
+
|
|
94
|
+
快速添加 skill 到项目。
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# 添加到所有已配置的工具
|
|
98
|
+
npx skillsmgr add code-review
|
|
99
|
+
|
|
100
|
+
# 添加到特定工具
|
|
101
|
+
npx skillsmgr add code-review --tool claude-code
|
|
102
|
+
|
|
103
|
+
# 使用复制模式而非符号链接
|
|
104
|
+
npx skillsmgr add code-review --copy
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### `npx skillsmgr remove <skill>`
|
|
108
|
+
|
|
109
|
+
从项目移除 skill。
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# 从所有工具移除
|
|
113
|
+
npx skillsmgr remove code-review
|
|
114
|
+
|
|
115
|
+
# 从特定工具移除
|
|
116
|
+
npx skillsmgr remove code-review --tool claude-code
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `npx skillsmgr sync`
|
|
120
|
+
|
|
121
|
+
同步并验证已部署的 skills。
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npx skillsmgr sync
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## 目录结构
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
~/.skills-manager/
|
|
131
|
+
├── official/ # 官方 skills (anthropic/skills)
|
|
132
|
+
│ └── anthropic/
|
|
133
|
+
│ ├── code-review/
|
|
134
|
+
│ └── tdd/
|
|
135
|
+
├── community/ # 社区 skills (其他仓库)
|
|
136
|
+
│ └── awesome-skills/
|
|
137
|
+
│ └── react-patterns/
|
|
138
|
+
└── custom/ # 本地自定义 skills
|
|
139
|
+
└── my-skill/
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## 特性
|
|
143
|
+
|
|
144
|
+
- **统一管理**:在一处管理所有 skills
|
|
145
|
+
- **多工具支持**:部署到 9 种不同的 AI 工具
|
|
146
|
+
- **默认符号链接**:修改自动同步
|
|
147
|
+
- **搜索过滤**:快速搜索大型 skill 仓库
|
|
148
|
+
- **进度指示**:下载时显示可视化反馈
|
|
149
|
+
- **增量更新**:无需完全重新部署即可添加/移除 skills
|
|
150
|
+
|
|
151
|
+
## 许可证
|
|
152
|
+
|
|
153
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -13,16 +13,17 @@ import { dirname as dirname2 } from "path";
|
|
|
13
13
|
import { homedir } from "os";
|
|
14
14
|
import { join } from "path";
|
|
15
15
|
var SKILLS_MANAGER_DIR = join(homedir(), ".skills-manager");
|
|
16
|
-
var METADATA_FILENAME = ".skillsmgr.json";
|
|
17
16
|
var SKILL_SOURCES = ["official", "community", "custom"];
|
|
18
17
|
var SUPPORTED_TOOLS = [
|
|
18
|
+
"antigravity",
|
|
19
|
+
"roo-code",
|
|
19
20
|
"claude-code",
|
|
20
|
-
"
|
|
21
|
-
"windsurf",
|
|
21
|
+
"opencode",
|
|
22
22
|
"cline",
|
|
23
|
-
"
|
|
23
|
+
"cursor",
|
|
24
24
|
"kilo-code",
|
|
25
|
-
"
|
|
25
|
+
"trae",
|
|
26
|
+
"windsurf"
|
|
26
27
|
];
|
|
27
28
|
var ANTHROPIC_SKILLS_REPO = "https://github.com/anthropics/skills";
|
|
28
29
|
|
|
@@ -37,7 +38,8 @@ import {
|
|
|
37
38
|
readdirSync,
|
|
38
39
|
unlinkSync,
|
|
39
40
|
writeFileSync,
|
|
40
|
-
rmSync
|
|
41
|
+
rmSync,
|
|
42
|
+
readlinkSync
|
|
41
43
|
} from "fs";
|
|
42
44
|
import { dirname, join as join2 } from "path";
|
|
43
45
|
function ensureDir(dir) {
|
|
@@ -56,13 +58,16 @@ function isSymlink(path) {
|
|
|
56
58
|
return false;
|
|
57
59
|
}
|
|
58
60
|
}
|
|
61
|
+
function readSymlinkTarget(path) {
|
|
62
|
+
try {
|
|
63
|
+
return readlinkSync(path);
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
59
68
|
function readFileContent(path) {
|
|
60
69
|
return readFileSync(path, "utf-8");
|
|
61
70
|
}
|
|
62
|
-
function writeFile(path, content) {
|
|
63
|
-
ensureDir(dirname(path));
|
|
64
|
-
writeFileSync(path, content, "utf-8");
|
|
65
|
-
}
|
|
66
71
|
function fileExists(path) {
|
|
67
72
|
return existsSync(path);
|
|
68
73
|
}
|
|
@@ -365,6 +370,22 @@ import inquirer from "inquirer";
|
|
|
365
370
|
|
|
366
371
|
// src/tools/configs.ts
|
|
367
372
|
var TOOL_CONFIGS = {
|
|
373
|
+
"antigravity": {
|
|
374
|
+
name: "antigravity",
|
|
375
|
+
displayName: "Antigravity",
|
|
376
|
+
skillsDir: ".agent/skills",
|
|
377
|
+
supportsLink: true,
|
|
378
|
+
supportsModeSpecific: false
|
|
379
|
+
},
|
|
380
|
+
"roo-code": {
|
|
381
|
+
name: "roo-code",
|
|
382
|
+
displayName: "Roo Code",
|
|
383
|
+
skillsDir: ".roo/skills",
|
|
384
|
+
supportsLink: true,
|
|
385
|
+
supportsModeSpecific: true,
|
|
386
|
+
modePattern: "skills-{mode}",
|
|
387
|
+
availableModes: ["code", "architect"]
|
|
388
|
+
},
|
|
368
389
|
"claude-code": {
|
|
369
390
|
name: "claude-code",
|
|
370
391
|
displayName: "Claude Code",
|
|
@@ -372,17 +393,10 @@ var TOOL_CONFIGS = {
|
|
|
372
393
|
supportsLink: true,
|
|
373
394
|
supportsModeSpecific: false
|
|
374
395
|
},
|
|
375
|
-
"
|
|
376
|
-
name: "
|
|
377
|
-
displayName: "
|
|
378
|
-
skillsDir: ".
|
|
379
|
-
supportsLink: true,
|
|
380
|
-
supportsModeSpecific: false
|
|
381
|
-
},
|
|
382
|
-
"windsurf": {
|
|
383
|
-
name: "windsurf",
|
|
384
|
-
displayName: "Windsurf",
|
|
385
|
-
skillsDir: ".windsurf/skills",
|
|
396
|
+
"opencode": {
|
|
397
|
+
name: "opencode",
|
|
398
|
+
displayName: "OpenCode",
|
|
399
|
+
skillsDir: ".opencode/skills",
|
|
386
400
|
supportsLink: true,
|
|
387
401
|
supportsModeSpecific: false
|
|
388
402
|
},
|
|
@@ -393,14 +407,12 @@ var TOOL_CONFIGS = {
|
|
|
393
407
|
supportsLink: true,
|
|
394
408
|
supportsModeSpecific: false
|
|
395
409
|
},
|
|
396
|
-
"
|
|
397
|
-
name: "
|
|
398
|
-
displayName: "
|
|
399
|
-
skillsDir: ".
|
|
410
|
+
"cursor": {
|
|
411
|
+
name: "cursor",
|
|
412
|
+
displayName: "Cursor",
|
|
413
|
+
skillsDir: ".cursor/skills",
|
|
400
414
|
supportsLink: true,
|
|
401
|
-
supportsModeSpecific:
|
|
402
|
-
modePattern: "skills-{mode}",
|
|
403
|
-
availableModes: ["code", "architect"]
|
|
415
|
+
supportsModeSpecific: false
|
|
404
416
|
},
|
|
405
417
|
"kilo-code": {
|
|
406
418
|
name: "kilo-code",
|
|
@@ -411,10 +423,17 @@ var TOOL_CONFIGS = {
|
|
|
411
423
|
modePattern: "skills-{mode}",
|
|
412
424
|
availableModes: ["code", "architect"]
|
|
413
425
|
},
|
|
414
|
-
"
|
|
415
|
-
name: "
|
|
416
|
-
displayName: "
|
|
417
|
-
skillsDir: ".
|
|
426
|
+
"trae": {
|
|
427
|
+
name: "trae",
|
|
428
|
+
displayName: "Trae",
|
|
429
|
+
skillsDir: ".trae/skills",
|
|
430
|
+
supportsLink: true,
|
|
431
|
+
supportsModeSpecific: false
|
|
432
|
+
},
|
|
433
|
+
"windsurf": {
|
|
434
|
+
name: "windsurf",
|
|
435
|
+
displayName: "Windsurf",
|
|
436
|
+
skillsDir: ".windsurf/skills",
|
|
418
437
|
supportsLink: true,
|
|
419
438
|
supportsModeSpecific: false
|
|
420
439
|
}
|
|
@@ -1183,61 +1202,144 @@ var SkillsService = class {
|
|
|
1183
1202
|
}
|
|
1184
1203
|
};
|
|
1185
1204
|
|
|
1186
|
-
// src/services/
|
|
1205
|
+
// src/services/scanner.ts
|
|
1187
1206
|
import { join as join8 } from "path";
|
|
1188
|
-
|
|
1189
|
-
|
|
1207
|
+
import { readdirSync as readdirSync2 } from "fs";
|
|
1208
|
+
var DeploymentScanner = class {
|
|
1209
|
+
constructor(projectDir, skillsManagerDir = SKILLS_MANAGER_DIR) {
|
|
1190
1210
|
this.projectDir = projectDir;
|
|
1191
|
-
this.
|
|
1211
|
+
this.skillsManagerDir = skillsManagerDir;
|
|
1212
|
+
this.skillsService = new SkillsService(this.skillsManagerDir);
|
|
1213
|
+
}
|
|
1214
|
+
skillsService;
|
|
1215
|
+
scanAllTools() {
|
|
1216
|
+
const deployments = [];
|
|
1217
|
+
for (const toolName of SUPPORTED_TOOLS) {
|
|
1218
|
+
const config = TOOL_CONFIGS[toolName];
|
|
1219
|
+
const toolDeployments = this.scanToolDeployment(toolName, config);
|
|
1220
|
+
deployments.push(...toolDeployments);
|
|
1221
|
+
}
|
|
1222
|
+
return deployments.filter((d) => d.skills.length > 0);
|
|
1223
|
+
}
|
|
1224
|
+
scanToolDeployment(toolName, config) {
|
|
1225
|
+
const deployments = [];
|
|
1226
|
+
const baseDir = join8(this.projectDir, config.skillsDir);
|
|
1227
|
+
const baseDeployment = this.scanDirectory(toolName, baseDir, config.skillsDir);
|
|
1228
|
+
if (baseDeployment.skills.length > 0) {
|
|
1229
|
+
deployments.push(baseDeployment);
|
|
1230
|
+
}
|
|
1231
|
+
if (config.supportsModeSpecific && config.availableModes) {
|
|
1232
|
+
for (const mode of config.availableModes) {
|
|
1233
|
+
const modeDir = getTargetDir(config, mode);
|
|
1234
|
+
const fullModeDir = join8(this.projectDir, modeDir);
|
|
1235
|
+
const modeDeployment = this.scanDirectory(toolName, fullModeDir, modeDir, mode);
|
|
1236
|
+
if (modeDeployment.skills.length > 0) {
|
|
1237
|
+
deployments.push(modeDeployment);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
return deployments;
|
|
1192
1242
|
}
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1243
|
+
getConfiguredTools() {
|
|
1244
|
+
const tools = /* @__PURE__ */ new Set();
|
|
1245
|
+
for (const toolName of SUPPORTED_TOOLS) {
|
|
1246
|
+
const config = TOOL_CONFIGS[toolName];
|
|
1247
|
+
const deployments = this.scanToolDeployment(toolName, config);
|
|
1248
|
+
if (deployments.some((d) => d.skills.length > 0)) {
|
|
1249
|
+
tools.add(toolName);
|
|
1250
|
+
}
|
|
1197
1251
|
}
|
|
1198
|
-
|
|
1199
|
-
return JSON.parse(content);
|
|
1252
|
+
return Array.from(tools);
|
|
1200
1253
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1254
|
+
isToolConfigured(toolName) {
|
|
1255
|
+
const config = TOOL_CONFIGS[toolName];
|
|
1256
|
+
if (!config) return false;
|
|
1257
|
+
const deployments = this.scanToolDeployment(toolName, config);
|
|
1258
|
+
return deployments.some((d) => d.skills.length > 0);
|
|
1203
1259
|
}
|
|
1204
|
-
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
|
|
1260
|
+
getDeployedSkills(toolName) {
|
|
1261
|
+
const config = TOOL_CONFIGS[toolName];
|
|
1262
|
+
if (!config) return [];
|
|
1263
|
+
const deployments = this.scanToolDeployment(toolName, config);
|
|
1264
|
+
return deployments.flatMap((d) => d.skills);
|
|
1265
|
+
}
|
|
1266
|
+
scanDirectory(toolName, fullPath, relativePath, mode) {
|
|
1267
|
+
const deployment = {
|
|
1268
|
+
toolName,
|
|
1269
|
+
targetDir: relativePath,
|
|
1208
1270
|
mode,
|
|
1209
|
-
|
|
1210
|
-
skills
|
|
1271
|
+
skills: []
|
|
1211
1272
|
};
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
updateDeployment(toolName, skills) {
|
|
1215
|
-
const metadata = this.load();
|
|
1216
|
-
if (metadata.tools[toolName]) {
|
|
1217
|
-
metadata.tools[toolName].skills = skills;
|
|
1218
|
-
metadata.tools[toolName].deployedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1273
|
+
if (!fileExists(fullPath)) {
|
|
1274
|
+
return deployment;
|
|
1219
1275
|
}
|
|
1220
|
-
|
|
1276
|
+
try {
|
|
1277
|
+
const entries = readdirSync2(fullPath, { withFileTypes: true });
|
|
1278
|
+
for (const entry of entries) {
|
|
1279
|
+
const skillPath = join8(fullPath, entry.name);
|
|
1280
|
+
const scanned = this.scanSkill(skillPath, entry.name);
|
|
1281
|
+
if (scanned) {
|
|
1282
|
+
deployment.skills.push(scanned);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
} catch {
|
|
1286
|
+
}
|
|
1287
|
+
return deployment;
|
|
1221
1288
|
}
|
|
1222
|
-
|
|
1223
|
-
const
|
|
1224
|
-
|
|
1225
|
-
|
|
1289
|
+
scanSkill(skillPath, name) {
|
|
1290
|
+
const skillMdPath = join8(skillPath, "SKILL.md");
|
|
1291
|
+
if (!fileExists(skillMdPath)) {
|
|
1292
|
+
return null;
|
|
1293
|
+
}
|
|
1294
|
+
if (isSymlink(skillPath)) {
|
|
1295
|
+
return this.scanLinkedSkill(skillPath, name);
|
|
1296
|
+
} else {
|
|
1297
|
+
return this.scanCopiedSkill(skillPath, name);
|
|
1298
|
+
}
|
|
1226
1299
|
}
|
|
1227
|
-
|
|
1228
|
-
const
|
|
1229
|
-
|
|
1300
|
+
scanLinkedSkill(skillPath, name) {
|
|
1301
|
+
const linkTarget = readSymlinkTarget(skillPath);
|
|
1302
|
+
if (!linkTarget) {
|
|
1303
|
+
return null;
|
|
1304
|
+
}
|
|
1305
|
+
const source = this.extractSourceFromPath(linkTarget);
|
|
1306
|
+
return {
|
|
1307
|
+
name,
|
|
1308
|
+
source: source || "unknown",
|
|
1309
|
+
deployMode: "link",
|
|
1310
|
+
path: skillPath
|
|
1311
|
+
};
|
|
1230
1312
|
}
|
|
1231
|
-
|
|
1232
|
-
const
|
|
1233
|
-
return
|
|
1313
|
+
scanCopiedSkill(skillPath, name) {
|
|
1314
|
+
const source = this.findSourceByName(name);
|
|
1315
|
+
return {
|
|
1316
|
+
name,
|
|
1317
|
+
source: source || "unknown",
|
|
1318
|
+
deployMode: "copy",
|
|
1319
|
+
path: skillPath
|
|
1320
|
+
};
|
|
1234
1321
|
}
|
|
1235
|
-
|
|
1236
|
-
const
|
|
1237
|
-
|
|
1322
|
+
extractSourceFromPath(linkTarget) {
|
|
1323
|
+
const normalizedTarget = linkTarget.replace(/\\/g, "/");
|
|
1324
|
+
const skillsManagerPattern = ".skills-manager/";
|
|
1325
|
+
const idx = normalizedTarget.indexOf(skillsManagerPattern);
|
|
1326
|
+
if (idx === -1) return null;
|
|
1327
|
+
const afterManager = normalizedTarget.substring(idx + skillsManagerPattern.length);
|
|
1328
|
+
const parts = afterManager.split("/");
|
|
1329
|
+
if (parts[0] === "custom") {
|
|
1330
|
+
return "custom";
|
|
1331
|
+
}
|
|
1332
|
+
if (parts.length >= 2) {
|
|
1333
|
+
return `${parts[0]}/${parts[1]}`;
|
|
1334
|
+
}
|
|
1335
|
+
return null;
|
|
1238
1336
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1337
|
+
findSourceByName(skillName) {
|
|
1338
|
+
const matches = this.skillsService.findSkillsByName(skillName);
|
|
1339
|
+
if (matches.length === 0) {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
return matches[0].source;
|
|
1241
1343
|
}
|
|
1242
1344
|
};
|
|
1243
1345
|
|
|
@@ -1272,26 +1374,25 @@ async function listAvailable() {
|
|
|
1272
1374
|
for (const [source, sourceSkills] of Object.entries(grouped)) {
|
|
1273
1375
|
console.log(`\u2500\u2500 ${source} (${sourceSkills.length} skill${sourceSkills.length > 1 ? "s" : ""}) \u2500\u2500`);
|
|
1274
1376
|
for (const skill of sourceSkills) {
|
|
1275
|
-
console.log(` ${skill.name
|
|
1377
|
+
console.log(` ${skill.name}`);
|
|
1276
1378
|
}
|
|
1277
1379
|
console.log();
|
|
1278
1380
|
}
|
|
1279
1381
|
}
|
|
1280
1382
|
async function listDeployed() {
|
|
1281
|
-
const
|
|
1282
|
-
|
|
1383
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1384
|
+
const deployments = scanner.scanAllTools();
|
|
1385
|
+
if (deployments.length === 0) {
|
|
1283
1386
|
console.log("No skills deployed in current project.");
|
|
1284
1387
|
console.log("\nRun: skillsmgr init");
|
|
1285
1388
|
return;
|
|
1286
1389
|
}
|
|
1287
1390
|
console.log("Deployed skills in current project:\n");
|
|
1288
|
-
const
|
|
1289
|
-
|
|
1290
|
-
const
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
const displayName = config?.displayName || toolName;
|
|
1294
|
-
console.log(`${displayName} (${deployment.targetDir}/):`);
|
|
1391
|
+
for (const deployment of deployments) {
|
|
1392
|
+
const config = TOOL_CONFIGS[deployment.toolName];
|
|
1393
|
+
const displayName = config?.displayName || deployment.toolName;
|
|
1394
|
+
const dirSuffix = deployment.mode && deployment.mode !== "all" ? ` [${deployment.mode}]` : "";
|
|
1395
|
+
console.log(`${displayName} (${deployment.targetDir}/)${dirSuffix}:`);
|
|
1295
1396
|
for (const skill of deployment.skills) {
|
|
1296
1397
|
const modeStr = skill.deployMode === "link" ? "link" : "copy";
|
|
1297
1398
|
console.log(` \u25C9 ${skill.name.padEnd(16)} (${modeStr}) \u2190 ${skill.source}`);
|
|
@@ -1353,14 +1454,14 @@ async function executeInit(options) {
|
|
|
1353
1454
|
process.exit(1);
|
|
1354
1455
|
}
|
|
1355
1456
|
const skillsService = new SkillsService(SKILLS_MANAGER_DIR);
|
|
1356
|
-
const
|
|
1457
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1357
1458
|
const deployer = new Deployer(process.cwd());
|
|
1358
1459
|
const allSkills = skillsService.getAllSkills();
|
|
1359
1460
|
if (allSkills.length === 0) {
|
|
1360
1461
|
console.log("No skills found. Run: skillsmgr install anthropic");
|
|
1361
1462
|
process.exit(1);
|
|
1362
1463
|
}
|
|
1363
|
-
const configuredTools =
|
|
1464
|
+
const configuredTools = scanner.getConfiguredTools();
|
|
1364
1465
|
const selectedTools = await promptTools(configuredTools);
|
|
1365
1466
|
const toolModes = {};
|
|
1366
1467
|
for (const toolName of selectedTools) {
|
|
@@ -1374,7 +1475,7 @@ async function executeInit(options) {
|
|
|
1374
1475
|
}
|
|
1375
1476
|
const deployedSkillNames = /* @__PURE__ */ new Set();
|
|
1376
1477
|
for (const toolName of selectedTools) {
|
|
1377
|
-
const deployed =
|
|
1478
|
+
const deployed = scanner.getDeployedSkills(toolName);
|
|
1378
1479
|
deployed.forEach((s) => deployedSkillNames.add(s.name));
|
|
1379
1480
|
}
|
|
1380
1481
|
const selectedSkillNames = await promptSkills(
|
|
@@ -1393,7 +1494,7 @@ async function executeInit(options) {
|
|
|
1393
1494
|
const mode = toolModes[toolName];
|
|
1394
1495
|
const targetDir = getTargetDir(config, mode);
|
|
1395
1496
|
console.log(`${config.displayName}:`);
|
|
1396
|
-
const previouslyDeployed =
|
|
1497
|
+
const previouslyDeployed = scanner.getDeployedSkills(toolName);
|
|
1397
1498
|
const previousNames = new Set(previouslyDeployed.map((s) => s.name));
|
|
1398
1499
|
const toAdd = selectedSkills.filter((s) => !previousNames.has(s.name));
|
|
1399
1500
|
const toKeep = selectedSkills.filter((s) => previousNames.has(s.name));
|
|
@@ -1411,12 +1512,6 @@ async function executeInit(options) {
|
|
|
1411
1512
|
deployer.deploySkill(skill, config, deployMode, mode);
|
|
1412
1513
|
console.log(` \u2713 ${skill.name} (${deployMode === "link" ? "linked" : "copied"})`);
|
|
1413
1514
|
}
|
|
1414
|
-
const newDeployedSkills = selectedSkills.map((skill) => ({
|
|
1415
|
-
name: skill.name,
|
|
1416
|
-
source: skill.source,
|
|
1417
|
-
deployMode
|
|
1418
|
-
}));
|
|
1419
|
-
metadataService.addDeployment(toolName, targetDir, mode, newDeployedSkills);
|
|
1420
1515
|
console.log();
|
|
1421
1516
|
}
|
|
1422
1517
|
console.log(
|
|
@@ -1435,7 +1530,7 @@ async function executeAdd(skillName, options) {
|
|
|
1435
1530
|
process.exit(1);
|
|
1436
1531
|
}
|
|
1437
1532
|
const skillsService = new SkillsService(SKILLS_MANAGER_DIR);
|
|
1438
|
-
const
|
|
1533
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1439
1534
|
const deployer = new Deployer(process.cwd());
|
|
1440
1535
|
const matchingSkills = skillsService.findSkillsByName(skillName);
|
|
1441
1536
|
if (matchingSkills.length === 0) {
|
|
@@ -1460,7 +1555,7 @@ async function executeAdd(skillName, options) {
|
|
|
1460
1555
|
}
|
|
1461
1556
|
targetTools = [options.tool];
|
|
1462
1557
|
} else {
|
|
1463
|
-
targetTools =
|
|
1558
|
+
targetTools = scanner.getConfiguredTools();
|
|
1464
1559
|
if (targetTools.length === 0) {
|
|
1465
1560
|
console.log("No tools configured. Run: skillsmgr init");
|
|
1466
1561
|
process.exit(1);
|
|
@@ -1470,19 +1565,15 @@ async function executeAdd(skillName, options) {
|
|
|
1470
1565
|
console.log(`Adding ${skillName} to configured tools...`);
|
|
1471
1566
|
for (const toolName of targetTools) {
|
|
1472
1567
|
const config = TOOL_CONFIGS[toolName];
|
|
1473
|
-
const
|
|
1474
|
-
const mode =
|
|
1475
|
-
|
|
1476
|
-
const existingSkills = metadataService.getDeployedSkills(toolName);
|
|
1568
|
+
const deployments = scanner.scanToolDeployment(toolName, config);
|
|
1569
|
+
const mode = deployments.length > 0 && deployments[0].mode ? deployments[0].mode : "all";
|
|
1570
|
+
const existingSkills = scanner.getDeployedSkills(toolName);
|
|
1477
1571
|
const alreadyExists = existingSkills.some((s) => s.name === skill.name);
|
|
1478
|
-
if (
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
source: skill.source,
|
|
1482
|
-
deployMode
|
|
1483
|
-
};
|
|
1484
|
-
metadataService.updateDeployment(toolName, [...existingSkills, newSkill]);
|
|
1572
|
+
if (alreadyExists) {
|
|
1573
|
+
console.log(` \xB7 ${config.displayName} (already deployed)`);
|
|
1574
|
+
continue;
|
|
1485
1575
|
}
|
|
1576
|
+
deployer.deploySkill(skill, config, deployMode, mode);
|
|
1486
1577
|
console.log(
|
|
1487
1578
|
` \u2713 ${config.displayName} (${deployMode === "link" ? "linked" : "copied"})`
|
|
1488
1579
|
);
|
|
@@ -1495,9 +1586,10 @@ var addCommand = new Command5("add").description("Add a skill to the project").a
|
|
|
1495
1586
|
// src/commands/remove.ts
|
|
1496
1587
|
import { Command as Command6 } from "commander";
|
|
1497
1588
|
async function executeRemove(skillName, options) {
|
|
1498
|
-
const
|
|
1589
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1499
1590
|
const deployer = new Deployer(process.cwd());
|
|
1500
|
-
|
|
1591
|
+
const configuredTools = scanner.getConfiguredTools();
|
|
1592
|
+
if (configuredTools.length === 0) {
|
|
1501
1593
|
console.log("No skills deployed in current project.");
|
|
1502
1594
|
process.exit(1);
|
|
1503
1595
|
}
|
|
@@ -1509,22 +1601,21 @@ async function executeRemove(skillName, options) {
|
|
|
1509
1601
|
}
|
|
1510
1602
|
targetTools = [options.tool];
|
|
1511
1603
|
} else {
|
|
1512
|
-
targetTools =
|
|
1604
|
+
targetTools = configuredTools;
|
|
1513
1605
|
}
|
|
1514
1606
|
console.log(`Removing ${skillName}...`);
|
|
1515
1607
|
let removed = false;
|
|
1516
1608
|
for (const toolName of targetTools) {
|
|
1517
1609
|
const config = TOOL_CONFIGS[toolName];
|
|
1518
|
-
const
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
removed = true;
|
|
1610
|
+
const deployments = scanner.scanToolDeployment(toolName, config);
|
|
1611
|
+
for (const deployment of deployments) {
|
|
1612
|
+
const skillToRemove = deployment.skills.find((s) => s.name === skillName);
|
|
1613
|
+
if (!skillToRemove) continue;
|
|
1614
|
+
const mode = deployment.mode || "all";
|
|
1615
|
+
deployer.removeSkill(skillName, config, mode);
|
|
1616
|
+
console.log(` \u2713 Removed from ${config.displayName}`);
|
|
1617
|
+
removed = true;
|
|
1618
|
+
}
|
|
1528
1619
|
}
|
|
1529
1620
|
if (!removed) {
|
|
1530
1621
|
console.log(`Skill '${skillName}' not found in any configured tool`);
|
|
@@ -1538,32 +1629,35 @@ var removeCommand = new Command6("remove").description("Remove a skill from the
|
|
|
1538
1629
|
import { Command as Command7 } from "commander";
|
|
1539
1630
|
import { join as join10 } from "path";
|
|
1540
1631
|
async function executeSync() {
|
|
1541
|
-
const
|
|
1632
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1542
1633
|
const deployer = new Deployer(process.cwd());
|
|
1543
|
-
|
|
1634
|
+
const skillsService = new SkillsService(SKILLS_MANAGER_DIR);
|
|
1635
|
+
const deployments = scanner.scanAllTools();
|
|
1636
|
+
if (deployments.length === 0) {
|
|
1544
1637
|
console.log("No skills deployed in current project.");
|
|
1545
1638
|
process.exit(1);
|
|
1546
1639
|
}
|
|
1547
1640
|
console.log("Checking deployed skills...\n");
|
|
1548
|
-
const configuredTools = metadataService.getConfiguredTools();
|
|
1549
1641
|
let updatedCount = 0;
|
|
1550
1642
|
let removedCount = 0;
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
const
|
|
1554
|
-
const deployment = metadataService.getToolDeployment(toolName);
|
|
1555
|
-
if (!deployment) continue;
|
|
1643
|
+
for (const deployment of deployments) {
|
|
1644
|
+
const config = TOOL_CONFIGS[deployment.toolName];
|
|
1645
|
+
const mode = deployment.mode || "all";
|
|
1556
1646
|
console.log(`${config.displayName} (${deployment.targetDir}/):`);
|
|
1557
1647
|
for (const skill of deployment.skills) {
|
|
1558
|
-
const deployedPath =
|
|
1559
|
-
|
|
1560
|
-
if (
|
|
1561
|
-
|
|
1648
|
+
const deployedPath = skill.path;
|
|
1649
|
+
let sourcePath = null;
|
|
1650
|
+
if (skill.source !== "unknown") {
|
|
1651
|
+
const sourceSkill = skillsService.getSkillByName(skill.name);
|
|
1652
|
+
if (sourceSkill) {
|
|
1653
|
+
sourcePath = sourceSkill.path;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
if (!sourcePath || !fileExists(sourcePath)) {
|
|
1657
|
+
console.log(` \u2717 ${skill.name}: orphaned (source not found)`);
|
|
1562
1658
|
const action = await promptOrphanAction(skill.name);
|
|
1563
1659
|
if (action === "remove") {
|
|
1564
|
-
deployer.removeSkill(skill.name, config,
|
|
1565
|
-
const remaining = deployment.skills.filter((s) => s.name !== skill.name);
|
|
1566
|
-
metadataService.updateDeployment(toolName, remaining);
|
|
1660
|
+
deployer.removeSkill(skill.name, config, mode);
|
|
1567
1661
|
console.log(` \u2713 Removed ${skill.name}`);
|
|
1568
1662
|
removedCount++;
|
|
1569
1663
|
}
|
|
@@ -1594,7 +1688,7 @@ async function executeSync() {
|
|
|
1594
1688
|
{ name: skill.name, description: "", path: sourcePath, source: skill.source },
|
|
1595
1689
|
config,
|
|
1596
1690
|
"copy",
|
|
1597
|
-
|
|
1691
|
+
mode
|
|
1598
1692
|
);
|
|
1599
1693
|
console.log(` \u2713 Updated ${skill.name}`);
|
|
1600
1694
|
updatedCount++;
|
|
@@ -1604,7 +1698,7 @@ async function executeSync() {
|
|
|
1604
1698
|
{ name: skill.name, description: "", path: sourcePath, source: skill.source },
|
|
1605
1699
|
config,
|
|
1606
1700
|
"copy",
|
|
1607
|
-
|
|
1701
|
+
mode
|
|
1608
1702
|
);
|
|
1609
1703
|
console.log(` \u2713 Updated ${skill.name}`);
|
|
1610
1704
|
updatedCount++;
|
|
@@ -1618,7 +1712,7 @@ async function executeSync() {
|
|
|
1618
1712
|
console.log();
|
|
1619
1713
|
}
|
|
1620
1714
|
console.log(
|
|
1621
|
-
`Sync complete: ${updatedCount} updated, ${removedCount} removed
|
|
1715
|
+
`Sync complete: ${updatedCount} updated, ${removedCount} removed`
|
|
1622
1716
|
);
|
|
1623
1717
|
}
|
|
1624
1718
|
var syncCommand = new Command7("sync").description("Sync and verify deployed skills").action(async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillsmgr",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Unified skills manager for AI coding tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
"roo-code",
|
|
26
26
|
"kilo-code",
|
|
27
27
|
"antigravity",
|
|
28
|
+
"opencode",
|
|
29
|
+
"trae",
|
|
28
30
|
"cli"
|
|
29
31
|
],
|
|
30
32
|
"author": "jtianling <jtianling@gmail.com>",
|