skillsmgr 0.2.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 -39
- package/README.zh-CN.md +153 -0
- package/dist/index.js +189 -111
- package/package.json +1 -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,84 +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
|
-
Or use directly with npx (no installation required):
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
npx skillsmgr setup
|
|
27
|
-
npx skillsmgr install anthropic
|
|
28
|
-
```
|
|
29
|
-
|
|
30
21
|
## Quick Start
|
|
31
22
|
|
|
32
23
|
```bash
|
|
33
24
|
# Initialize skills manager
|
|
34
|
-
skillsmgr setup
|
|
25
|
+
npx skillsmgr setup
|
|
35
26
|
|
|
36
27
|
# Install official Anthropic skills
|
|
37
|
-
skillsmgr install anthropic
|
|
28
|
+
npx skillsmgr install anthropic
|
|
38
29
|
|
|
39
30
|
# Deploy skills to your project
|
|
40
31
|
cd your-project
|
|
41
|
-
skillsmgr init
|
|
32
|
+
npx skillsmgr init
|
|
42
33
|
```
|
|
43
34
|
|
|
44
35
|
## Commands
|
|
45
36
|
|
|
46
|
-
### `skillsmgr setup`
|
|
37
|
+
### `npx skillsmgr setup`
|
|
47
38
|
|
|
48
39
|
Initialize `~/.skills-manager/` directory structure with example skill.
|
|
49
40
|
|
|
50
41
|
```bash
|
|
51
|
-
skillsmgr setup
|
|
42
|
+
npx skillsmgr setup
|
|
52
43
|
```
|
|
53
44
|
|
|
54
|
-
### `skillsmgr install <source>`
|
|
45
|
+
### `npx skillsmgr install <source>`
|
|
55
46
|
|
|
56
47
|
Download skills from a repository.
|
|
57
48
|
|
|
58
49
|
```bash
|
|
59
50
|
# Install official Anthropic skills
|
|
60
|
-
skillsmgr install anthropic
|
|
51
|
+
npx skillsmgr install anthropic
|
|
61
52
|
|
|
62
53
|
# Install from any GitHub repository
|
|
63
|
-
skillsmgr install https://github.com/user/skills-repo
|
|
54
|
+
npx skillsmgr install https://github.com/user/skills-repo
|
|
64
55
|
|
|
65
56
|
# Install specific skill
|
|
66
|
-
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
|
|
67
58
|
|
|
68
59
|
# Install all skills without prompting
|
|
69
|
-
skillsmgr install anthropic --all
|
|
60
|
+
npx skillsmgr install anthropic --all
|
|
70
61
|
|
|
71
62
|
# Install to custom/ instead of community/
|
|
72
|
-
skillsmgr install https://github.com/user/repo --custom
|
|
63
|
+
npx skillsmgr install https://github.com/user/repo --custom
|
|
73
64
|
```
|
|
74
65
|
|
|
75
|
-
### `skillsmgr list`
|
|
66
|
+
### `npx skillsmgr list`
|
|
76
67
|
|
|
77
68
|
List available skills.
|
|
78
69
|
|
|
79
70
|
```bash
|
|
80
71
|
# List all available skills
|
|
81
|
-
skillsmgr list
|
|
72
|
+
npx skillsmgr list
|
|
82
73
|
|
|
83
74
|
# List deployed skills in current project
|
|
84
|
-
skillsmgr list --deployed
|
|
75
|
+
npx skillsmgr list --deployed
|
|
85
76
|
```
|
|
86
77
|
|
|
87
|
-
### `skillsmgr init`
|
|
78
|
+
### `npx skillsmgr init`
|
|
88
79
|
|
|
89
80
|
Interactive deployment of skills to current project.
|
|
90
81
|
|
|
91
82
|
```bash
|
|
92
|
-
skillsmgr init
|
|
83
|
+
npx skillsmgr init
|
|
93
84
|
```
|
|
94
85
|
|
|
95
86
|
Features:
|
|
@@ -98,39 +89,39 @@ Features:
|
|
|
98
89
|
- Choose skills to deploy with search filter
|
|
99
90
|
- Incremental updates (add/remove skills)
|
|
100
91
|
|
|
101
|
-
### `skillsmgr add <skill>`
|
|
92
|
+
### `npx skillsmgr add <skill>`
|
|
102
93
|
|
|
103
94
|
Quick add a skill to project.
|
|
104
95
|
|
|
105
96
|
```bash
|
|
106
97
|
# Add to all configured tools
|
|
107
|
-
skillsmgr add code-review
|
|
98
|
+
npx skillsmgr add code-review
|
|
108
99
|
|
|
109
100
|
# Add to specific tool
|
|
110
|
-
skillsmgr add code-review --tool claude-code
|
|
101
|
+
npx skillsmgr add code-review --tool claude-code
|
|
111
102
|
|
|
112
103
|
# Use copy mode instead of symlink
|
|
113
|
-
skillsmgr add code-review --copy
|
|
104
|
+
npx skillsmgr add code-review --copy
|
|
114
105
|
```
|
|
115
106
|
|
|
116
|
-
### `skillsmgr remove <skill>`
|
|
107
|
+
### `npx skillsmgr remove <skill>`
|
|
117
108
|
|
|
118
109
|
Remove a skill from project.
|
|
119
110
|
|
|
120
111
|
```bash
|
|
121
112
|
# Remove from all tools
|
|
122
|
-
skillsmgr remove code-review
|
|
113
|
+
npx skillsmgr remove code-review
|
|
123
114
|
|
|
124
115
|
# Remove from specific tool
|
|
125
|
-
skillsmgr remove code-review --tool claude-code
|
|
116
|
+
npx skillsmgr remove code-review --tool claude-code
|
|
126
117
|
```
|
|
127
118
|
|
|
128
|
-
### `skillsmgr sync`
|
|
119
|
+
### `npx skillsmgr sync`
|
|
129
120
|
|
|
130
121
|
Sync and verify deployed skills.
|
|
131
122
|
|
|
132
123
|
```bash
|
|
133
|
-
skillsmgr sync
|
|
124
|
+
npx skillsmgr sync
|
|
134
125
|
```
|
|
135
126
|
|
|
136
127
|
## Directory Structure
|
|
@@ -151,7 +142,7 @@ skillsmgr sync
|
|
|
151
142
|
## Features
|
|
152
143
|
|
|
153
144
|
- **Unified Management**: Manage all skills in one place
|
|
154
|
-
- **Multi-tool Support**: Deploy to
|
|
145
|
+
- **Multi-tool Support**: Deploy to 9 different AI tools
|
|
155
146
|
- **Symlink by Default**: Changes sync automatically
|
|
156
147
|
- **Search Filter**: Quick search for large skill repositories
|
|
157
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,7 +13,6 @@ 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 = [
|
|
19
18
|
"antigravity",
|
|
@@ -39,7 +38,8 @@ import {
|
|
|
39
38
|
readdirSync,
|
|
40
39
|
unlinkSync,
|
|
41
40
|
writeFileSync,
|
|
42
|
-
rmSync
|
|
41
|
+
rmSync,
|
|
42
|
+
readlinkSync
|
|
43
43
|
} from "fs";
|
|
44
44
|
import { dirname, join as join2 } from "path";
|
|
45
45
|
function ensureDir(dir) {
|
|
@@ -58,13 +58,16 @@ function isSymlink(path) {
|
|
|
58
58
|
return false;
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
function readSymlinkTarget(path) {
|
|
62
|
+
try {
|
|
63
|
+
return readlinkSync(path);
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
61
68
|
function readFileContent(path) {
|
|
62
69
|
return readFileSync(path, "utf-8");
|
|
63
70
|
}
|
|
64
|
-
function writeFile(path, content) {
|
|
65
|
-
ensureDir(dirname(path));
|
|
66
|
-
writeFileSync(path, content, "utf-8");
|
|
67
|
-
}
|
|
68
71
|
function fileExists(path) {
|
|
69
72
|
return existsSync(path);
|
|
70
73
|
}
|
|
@@ -1199,61 +1202,144 @@ var SkillsService = class {
|
|
|
1199
1202
|
}
|
|
1200
1203
|
};
|
|
1201
1204
|
|
|
1202
|
-
// src/services/
|
|
1205
|
+
// src/services/scanner.ts
|
|
1203
1206
|
import { join as join8 } from "path";
|
|
1204
|
-
|
|
1205
|
-
|
|
1207
|
+
import { readdirSync as readdirSync2 } from "fs";
|
|
1208
|
+
var DeploymentScanner = class {
|
|
1209
|
+
constructor(projectDir, skillsManagerDir = SKILLS_MANAGER_DIR) {
|
|
1206
1210
|
this.projectDir = projectDir;
|
|
1207
|
-
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;
|
|
1208
1242
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
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
|
+
}
|
|
1213
1251
|
}
|
|
1214
|
-
|
|
1215
|
-
return JSON.parse(content);
|
|
1252
|
+
return Array.from(tools);
|
|
1216
1253
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
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);
|
|
1219
1259
|
}
|
|
1220
|
-
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1223
|
-
|
|
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,
|
|
1224
1270
|
mode,
|
|
1225
|
-
|
|
1226
|
-
skills
|
|
1271
|
+
skills: []
|
|
1227
1272
|
};
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
updateDeployment(toolName, skills) {
|
|
1231
|
-
const metadata = this.load();
|
|
1232
|
-
if (metadata.tools[toolName]) {
|
|
1233
|
-
metadata.tools[toolName].skills = skills;
|
|
1234
|
-
metadata.tools[toolName].deployedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1273
|
+
if (!fileExists(fullPath)) {
|
|
1274
|
+
return deployment;
|
|
1235
1275
|
}
|
|
1236
|
-
|
|
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;
|
|
1237
1288
|
}
|
|
1238
|
-
|
|
1239
|
-
const
|
|
1240
|
-
|
|
1241
|
-
|
|
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
|
+
}
|
|
1242
1299
|
}
|
|
1243
|
-
|
|
1244
|
-
const
|
|
1245
|
-
|
|
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
|
+
};
|
|
1246
1312
|
}
|
|
1247
|
-
|
|
1248
|
-
const
|
|
1249
|
-
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
|
+
};
|
|
1250
1321
|
}
|
|
1251
|
-
|
|
1252
|
-
const
|
|
1253
|
-
|
|
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;
|
|
1254
1336
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1337
|
+
findSourceByName(skillName) {
|
|
1338
|
+
const matches = this.skillsService.findSkillsByName(skillName);
|
|
1339
|
+
if (matches.length === 0) {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
return matches[0].source;
|
|
1257
1343
|
}
|
|
1258
1344
|
};
|
|
1259
1345
|
|
|
@@ -1288,26 +1374,25 @@ async function listAvailable() {
|
|
|
1288
1374
|
for (const [source, sourceSkills] of Object.entries(grouped)) {
|
|
1289
1375
|
console.log(`\u2500\u2500 ${source} (${sourceSkills.length} skill${sourceSkills.length > 1 ? "s" : ""}) \u2500\u2500`);
|
|
1290
1376
|
for (const skill of sourceSkills) {
|
|
1291
|
-
console.log(` ${skill.name
|
|
1377
|
+
console.log(` ${skill.name}`);
|
|
1292
1378
|
}
|
|
1293
1379
|
console.log();
|
|
1294
1380
|
}
|
|
1295
1381
|
}
|
|
1296
1382
|
async function listDeployed() {
|
|
1297
|
-
const
|
|
1298
|
-
|
|
1383
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1384
|
+
const deployments = scanner.scanAllTools();
|
|
1385
|
+
if (deployments.length === 0) {
|
|
1299
1386
|
console.log("No skills deployed in current project.");
|
|
1300
1387
|
console.log("\nRun: skillsmgr init");
|
|
1301
1388
|
return;
|
|
1302
1389
|
}
|
|
1303
1390
|
console.log("Deployed skills in current project:\n");
|
|
1304
|
-
const
|
|
1305
|
-
|
|
1306
|
-
const
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
const displayName = config?.displayName || toolName;
|
|
1310
|
-
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}:`);
|
|
1311
1396
|
for (const skill of deployment.skills) {
|
|
1312
1397
|
const modeStr = skill.deployMode === "link" ? "link" : "copy";
|
|
1313
1398
|
console.log(` \u25C9 ${skill.name.padEnd(16)} (${modeStr}) \u2190 ${skill.source}`);
|
|
@@ -1369,14 +1454,14 @@ async function executeInit(options) {
|
|
|
1369
1454
|
process.exit(1);
|
|
1370
1455
|
}
|
|
1371
1456
|
const skillsService = new SkillsService(SKILLS_MANAGER_DIR);
|
|
1372
|
-
const
|
|
1457
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1373
1458
|
const deployer = new Deployer(process.cwd());
|
|
1374
1459
|
const allSkills = skillsService.getAllSkills();
|
|
1375
1460
|
if (allSkills.length === 0) {
|
|
1376
1461
|
console.log("No skills found. Run: skillsmgr install anthropic");
|
|
1377
1462
|
process.exit(1);
|
|
1378
1463
|
}
|
|
1379
|
-
const configuredTools =
|
|
1464
|
+
const configuredTools = scanner.getConfiguredTools();
|
|
1380
1465
|
const selectedTools = await promptTools(configuredTools);
|
|
1381
1466
|
const toolModes = {};
|
|
1382
1467
|
for (const toolName of selectedTools) {
|
|
@@ -1390,7 +1475,7 @@ async function executeInit(options) {
|
|
|
1390
1475
|
}
|
|
1391
1476
|
const deployedSkillNames = /* @__PURE__ */ new Set();
|
|
1392
1477
|
for (const toolName of selectedTools) {
|
|
1393
|
-
const deployed =
|
|
1478
|
+
const deployed = scanner.getDeployedSkills(toolName);
|
|
1394
1479
|
deployed.forEach((s) => deployedSkillNames.add(s.name));
|
|
1395
1480
|
}
|
|
1396
1481
|
const selectedSkillNames = await promptSkills(
|
|
@@ -1409,7 +1494,7 @@ async function executeInit(options) {
|
|
|
1409
1494
|
const mode = toolModes[toolName];
|
|
1410
1495
|
const targetDir = getTargetDir(config, mode);
|
|
1411
1496
|
console.log(`${config.displayName}:`);
|
|
1412
|
-
const previouslyDeployed =
|
|
1497
|
+
const previouslyDeployed = scanner.getDeployedSkills(toolName);
|
|
1413
1498
|
const previousNames = new Set(previouslyDeployed.map((s) => s.name));
|
|
1414
1499
|
const toAdd = selectedSkills.filter((s) => !previousNames.has(s.name));
|
|
1415
1500
|
const toKeep = selectedSkills.filter((s) => previousNames.has(s.name));
|
|
@@ -1427,12 +1512,6 @@ async function executeInit(options) {
|
|
|
1427
1512
|
deployer.deploySkill(skill, config, deployMode, mode);
|
|
1428
1513
|
console.log(` \u2713 ${skill.name} (${deployMode === "link" ? "linked" : "copied"})`);
|
|
1429
1514
|
}
|
|
1430
|
-
const newDeployedSkills = selectedSkills.map((skill) => ({
|
|
1431
|
-
name: skill.name,
|
|
1432
|
-
source: skill.source,
|
|
1433
|
-
deployMode
|
|
1434
|
-
}));
|
|
1435
|
-
metadataService.addDeployment(toolName, targetDir, mode, newDeployedSkills);
|
|
1436
1515
|
console.log();
|
|
1437
1516
|
}
|
|
1438
1517
|
console.log(
|
|
@@ -1451,7 +1530,7 @@ async function executeAdd(skillName, options) {
|
|
|
1451
1530
|
process.exit(1);
|
|
1452
1531
|
}
|
|
1453
1532
|
const skillsService = new SkillsService(SKILLS_MANAGER_DIR);
|
|
1454
|
-
const
|
|
1533
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1455
1534
|
const deployer = new Deployer(process.cwd());
|
|
1456
1535
|
const matchingSkills = skillsService.findSkillsByName(skillName);
|
|
1457
1536
|
if (matchingSkills.length === 0) {
|
|
@@ -1476,7 +1555,7 @@ async function executeAdd(skillName, options) {
|
|
|
1476
1555
|
}
|
|
1477
1556
|
targetTools = [options.tool];
|
|
1478
1557
|
} else {
|
|
1479
|
-
targetTools =
|
|
1558
|
+
targetTools = scanner.getConfiguredTools();
|
|
1480
1559
|
if (targetTools.length === 0) {
|
|
1481
1560
|
console.log("No tools configured. Run: skillsmgr init");
|
|
1482
1561
|
process.exit(1);
|
|
@@ -1486,19 +1565,15 @@ async function executeAdd(skillName, options) {
|
|
|
1486
1565
|
console.log(`Adding ${skillName} to configured tools...`);
|
|
1487
1566
|
for (const toolName of targetTools) {
|
|
1488
1567
|
const config = TOOL_CONFIGS[toolName];
|
|
1489
|
-
const
|
|
1490
|
-
const mode =
|
|
1491
|
-
|
|
1492
|
-
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);
|
|
1493
1571
|
const alreadyExists = existingSkills.some((s) => s.name === skill.name);
|
|
1494
|
-
if (
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
source: skill.source,
|
|
1498
|
-
deployMode
|
|
1499
|
-
};
|
|
1500
|
-
metadataService.updateDeployment(toolName, [...existingSkills, newSkill]);
|
|
1572
|
+
if (alreadyExists) {
|
|
1573
|
+
console.log(` \xB7 ${config.displayName} (already deployed)`);
|
|
1574
|
+
continue;
|
|
1501
1575
|
}
|
|
1576
|
+
deployer.deploySkill(skill, config, deployMode, mode);
|
|
1502
1577
|
console.log(
|
|
1503
1578
|
` \u2713 ${config.displayName} (${deployMode === "link" ? "linked" : "copied"})`
|
|
1504
1579
|
);
|
|
@@ -1511,9 +1586,10 @@ var addCommand = new Command5("add").description("Add a skill to the project").a
|
|
|
1511
1586
|
// src/commands/remove.ts
|
|
1512
1587
|
import { Command as Command6 } from "commander";
|
|
1513
1588
|
async function executeRemove(skillName, options) {
|
|
1514
|
-
const
|
|
1589
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1515
1590
|
const deployer = new Deployer(process.cwd());
|
|
1516
|
-
|
|
1591
|
+
const configuredTools = scanner.getConfiguredTools();
|
|
1592
|
+
if (configuredTools.length === 0) {
|
|
1517
1593
|
console.log("No skills deployed in current project.");
|
|
1518
1594
|
process.exit(1);
|
|
1519
1595
|
}
|
|
@@ -1525,22 +1601,21 @@ async function executeRemove(skillName, options) {
|
|
|
1525
1601
|
}
|
|
1526
1602
|
targetTools = [options.tool];
|
|
1527
1603
|
} else {
|
|
1528
|
-
targetTools =
|
|
1604
|
+
targetTools = configuredTools;
|
|
1529
1605
|
}
|
|
1530
1606
|
console.log(`Removing ${skillName}...`);
|
|
1531
1607
|
let removed = false;
|
|
1532
1608
|
for (const toolName of targetTools) {
|
|
1533
1609
|
const config = TOOL_CONFIGS[toolName];
|
|
1534
|
-
const
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
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
|
+
}
|
|
1544
1619
|
}
|
|
1545
1620
|
if (!removed) {
|
|
1546
1621
|
console.log(`Skill '${skillName}' not found in any configured tool`);
|
|
@@ -1554,32 +1629,35 @@ var removeCommand = new Command6("remove").description("Remove a skill from the
|
|
|
1554
1629
|
import { Command as Command7 } from "commander";
|
|
1555
1630
|
import { join as join10 } from "path";
|
|
1556
1631
|
async function executeSync() {
|
|
1557
|
-
const
|
|
1632
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1558
1633
|
const deployer = new Deployer(process.cwd());
|
|
1559
|
-
|
|
1634
|
+
const skillsService = new SkillsService(SKILLS_MANAGER_DIR);
|
|
1635
|
+
const deployments = scanner.scanAllTools();
|
|
1636
|
+
if (deployments.length === 0) {
|
|
1560
1637
|
console.log("No skills deployed in current project.");
|
|
1561
1638
|
process.exit(1);
|
|
1562
1639
|
}
|
|
1563
1640
|
console.log("Checking deployed skills...\n");
|
|
1564
|
-
const configuredTools = metadataService.getConfiguredTools();
|
|
1565
1641
|
let updatedCount = 0;
|
|
1566
1642
|
let removedCount = 0;
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
const
|
|
1570
|
-
const deployment = metadataService.getToolDeployment(toolName);
|
|
1571
|
-
if (!deployment) continue;
|
|
1643
|
+
for (const deployment of deployments) {
|
|
1644
|
+
const config = TOOL_CONFIGS[deployment.toolName];
|
|
1645
|
+
const mode = deployment.mode || "all";
|
|
1572
1646
|
console.log(`${config.displayName} (${deployment.targetDir}/):`);
|
|
1573
1647
|
for (const skill of deployment.skills) {
|
|
1574
|
-
const deployedPath =
|
|
1575
|
-
|
|
1576
|
-
if (
|
|
1577
|
-
|
|
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)`);
|
|
1578
1658
|
const action = await promptOrphanAction(skill.name);
|
|
1579
1659
|
if (action === "remove") {
|
|
1580
|
-
deployer.removeSkill(skill.name, config,
|
|
1581
|
-
const remaining = deployment.skills.filter((s) => s.name !== skill.name);
|
|
1582
|
-
metadataService.updateDeployment(toolName, remaining);
|
|
1660
|
+
deployer.removeSkill(skill.name, config, mode);
|
|
1583
1661
|
console.log(` \u2713 Removed ${skill.name}`);
|
|
1584
1662
|
removedCount++;
|
|
1585
1663
|
}
|
|
@@ -1610,7 +1688,7 @@ async function executeSync() {
|
|
|
1610
1688
|
{ name: skill.name, description: "", path: sourcePath, source: skill.source },
|
|
1611
1689
|
config,
|
|
1612
1690
|
"copy",
|
|
1613
|
-
|
|
1691
|
+
mode
|
|
1614
1692
|
);
|
|
1615
1693
|
console.log(` \u2713 Updated ${skill.name}`);
|
|
1616
1694
|
updatedCount++;
|
|
@@ -1620,7 +1698,7 @@ async function executeSync() {
|
|
|
1620
1698
|
{ name: skill.name, description: "", path: sourcePath, source: skill.source },
|
|
1621
1699
|
config,
|
|
1622
1700
|
"copy",
|
|
1623
|
-
|
|
1701
|
+
mode
|
|
1624
1702
|
);
|
|
1625
1703
|
console.log(` \u2713 Updated ${skill.name}`);
|
|
1626
1704
|
updatedCount++;
|
|
@@ -1634,7 +1712,7 @@ async function executeSync() {
|
|
|
1634
1712
|
console.log();
|
|
1635
1713
|
}
|
|
1636
1714
|
console.log(
|
|
1637
|
-
`Sync complete: ${updatedCount} updated, ${removedCount} removed
|
|
1715
|
+
`Sync complete: ${updatedCount} updated, ${removedCount} removed`
|
|
1638
1716
|
);
|
|
1639
1717
|
}
|
|
1640
1718
|
var syncCommand = new Command7("sync").description("Sync and verify deployed skills").action(async () => {
|