skillsmgr 0.2.0 → 0.3.1
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 +202 -112
- 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,148 @@ 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, conflict } = this.findSourceByName(name);
|
|
1315
|
+
return {
|
|
1316
|
+
name,
|
|
1317
|
+
source: source || "unknown",
|
|
1318
|
+
deployMode: "copy",
|
|
1319
|
+
path: skillPath,
|
|
1320
|
+
conflict
|
|
1321
|
+
};
|
|
1250
1322
|
}
|
|
1251
|
-
|
|
1252
|
-
const
|
|
1253
|
-
|
|
1323
|
+
extractSourceFromPath(linkTarget) {
|
|
1324
|
+
const normalizedTarget = linkTarget.replace(/\\/g, "/");
|
|
1325
|
+
const skillsManagerPattern = ".skills-manager/";
|
|
1326
|
+
const idx = normalizedTarget.indexOf(skillsManagerPattern);
|
|
1327
|
+
if (idx === -1) return null;
|
|
1328
|
+
const afterManager = normalizedTarget.substring(idx + skillsManagerPattern.length);
|
|
1329
|
+
const parts = afterManager.split("/");
|
|
1330
|
+
if (parts[0] === "custom") {
|
|
1331
|
+
return "custom";
|
|
1332
|
+
}
|
|
1333
|
+
if (parts.length >= 2) {
|
|
1334
|
+
return `${parts[0]}/${parts[1]}`;
|
|
1335
|
+
}
|
|
1336
|
+
return null;
|
|
1254
1337
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1338
|
+
findSourceByName(skillName) {
|
|
1339
|
+
const matches = this.skillsService.findSkillsByName(skillName);
|
|
1340
|
+
if (matches.length === 0) {
|
|
1341
|
+
return { source: null, conflict: false };
|
|
1342
|
+
}
|
|
1343
|
+
if (matches.length > 1) {
|
|
1344
|
+
return { source: null, conflict: true };
|
|
1345
|
+
}
|
|
1346
|
+
return { source: matches[0].source, conflict: false };
|
|
1257
1347
|
}
|
|
1258
1348
|
};
|
|
1259
1349
|
|
|
@@ -1288,29 +1378,32 @@ async function listAvailable() {
|
|
|
1288
1378
|
for (const [source, sourceSkills] of Object.entries(grouped)) {
|
|
1289
1379
|
console.log(`\u2500\u2500 ${source} (${sourceSkills.length} skill${sourceSkills.length > 1 ? "s" : ""}) \u2500\u2500`);
|
|
1290
1380
|
for (const skill of sourceSkills) {
|
|
1291
|
-
console.log(` ${skill.name
|
|
1381
|
+
console.log(` ${skill.name}`);
|
|
1292
1382
|
}
|
|
1293
1383
|
console.log();
|
|
1294
1384
|
}
|
|
1295
1385
|
}
|
|
1296
1386
|
async function listDeployed() {
|
|
1297
|
-
const
|
|
1298
|
-
|
|
1387
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1388
|
+
const deployments = scanner.scanAllTools();
|
|
1389
|
+
if (deployments.length === 0) {
|
|
1299
1390
|
console.log("No skills deployed in current project.");
|
|
1300
1391
|
console.log("\nRun: skillsmgr init");
|
|
1301
1392
|
return;
|
|
1302
1393
|
}
|
|
1303
1394
|
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}/):`);
|
|
1395
|
+
for (const deployment of deployments) {
|
|
1396
|
+
const config = TOOL_CONFIGS[deployment.toolName];
|
|
1397
|
+
const displayName = config?.displayName || deployment.toolName;
|
|
1398
|
+
const dirSuffix = deployment.mode && deployment.mode !== "all" ? ` [${deployment.mode}]` : "";
|
|
1399
|
+
console.log(`${displayName} (${deployment.targetDir}/)${dirSuffix}:`);
|
|
1311
1400
|
for (const skill of deployment.skills) {
|
|
1312
1401
|
const modeStr = skill.deployMode === "link" ? "link" : "copy";
|
|
1313
|
-
|
|
1402
|
+
if (skill.conflict) {
|
|
1403
|
+
console.log(` \u26A0 ${skill.name.padEnd(16)} (${modeStr}) \u2190 conflict`);
|
|
1404
|
+
} else {
|
|
1405
|
+
console.log(` \u25C9 ${skill.name.padEnd(16)} (${modeStr}) \u2190 ${skill.source}`);
|
|
1406
|
+
}
|
|
1314
1407
|
}
|
|
1315
1408
|
console.log();
|
|
1316
1409
|
}
|
|
@@ -1369,14 +1462,14 @@ async function executeInit(options) {
|
|
|
1369
1462
|
process.exit(1);
|
|
1370
1463
|
}
|
|
1371
1464
|
const skillsService = new SkillsService(SKILLS_MANAGER_DIR);
|
|
1372
|
-
const
|
|
1465
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1373
1466
|
const deployer = new Deployer(process.cwd());
|
|
1374
1467
|
const allSkills = skillsService.getAllSkills();
|
|
1375
1468
|
if (allSkills.length === 0) {
|
|
1376
1469
|
console.log("No skills found. Run: skillsmgr install anthropic");
|
|
1377
1470
|
process.exit(1);
|
|
1378
1471
|
}
|
|
1379
|
-
const configuredTools =
|
|
1472
|
+
const configuredTools = scanner.getConfiguredTools();
|
|
1380
1473
|
const selectedTools = await promptTools(configuredTools);
|
|
1381
1474
|
const toolModes = {};
|
|
1382
1475
|
for (const toolName of selectedTools) {
|
|
@@ -1390,7 +1483,7 @@ async function executeInit(options) {
|
|
|
1390
1483
|
}
|
|
1391
1484
|
const deployedSkillNames = /* @__PURE__ */ new Set();
|
|
1392
1485
|
for (const toolName of selectedTools) {
|
|
1393
|
-
const deployed =
|
|
1486
|
+
const deployed = scanner.getDeployedSkills(toolName);
|
|
1394
1487
|
deployed.forEach((s) => deployedSkillNames.add(s.name));
|
|
1395
1488
|
}
|
|
1396
1489
|
const selectedSkillNames = await promptSkills(
|
|
@@ -1409,7 +1502,7 @@ async function executeInit(options) {
|
|
|
1409
1502
|
const mode = toolModes[toolName];
|
|
1410
1503
|
const targetDir = getTargetDir(config, mode);
|
|
1411
1504
|
console.log(`${config.displayName}:`);
|
|
1412
|
-
const previouslyDeployed =
|
|
1505
|
+
const previouslyDeployed = scanner.getDeployedSkills(toolName);
|
|
1413
1506
|
const previousNames = new Set(previouslyDeployed.map((s) => s.name));
|
|
1414
1507
|
const toAdd = selectedSkills.filter((s) => !previousNames.has(s.name));
|
|
1415
1508
|
const toKeep = selectedSkills.filter((s) => previousNames.has(s.name));
|
|
@@ -1427,12 +1520,6 @@ async function executeInit(options) {
|
|
|
1427
1520
|
deployer.deploySkill(skill, config, deployMode, mode);
|
|
1428
1521
|
console.log(` \u2713 ${skill.name} (${deployMode === "link" ? "linked" : "copied"})`);
|
|
1429
1522
|
}
|
|
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
1523
|
console.log();
|
|
1437
1524
|
}
|
|
1438
1525
|
console.log(
|
|
@@ -1451,7 +1538,7 @@ async function executeAdd(skillName, options) {
|
|
|
1451
1538
|
process.exit(1);
|
|
1452
1539
|
}
|
|
1453
1540
|
const skillsService = new SkillsService(SKILLS_MANAGER_DIR);
|
|
1454
|
-
const
|
|
1541
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1455
1542
|
const deployer = new Deployer(process.cwd());
|
|
1456
1543
|
const matchingSkills = skillsService.findSkillsByName(skillName);
|
|
1457
1544
|
if (matchingSkills.length === 0) {
|
|
@@ -1476,7 +1563,7 @@ async function executeAdd(skillName, options) {
|
|
|
1476
1563
|
}
|
|
1477
1564
|
targetTools = [options.tool];
|
|
1478
1565
|
} else {
|
|
1479
|
-
targetTools =
|
|
1566
|
+
targetTools = scanner.getConfiguredTools();
|
|
1480
1567
|
if (targetTools.length === 0) {
|
|
1481
1568
|
console.log("No tools configured. Run: skillsmgr init");
|
|
1482
1569
|
process.exit(1);
|
|
@@ -1486,19 +1573,15 @@ async function executeAdd(skillName, options) {
|
|
|
1486
1573
|
console.log(`Adding ${skillName} to configured tools...`);
|
|
1487
1574
|
for (const toolName of targetTools) {
|
|
1488
1575
|
const config = TOOL_CONFIGS[toolName];
|
|
1489
|
-
const
|
|
1490
|
-
const mode =
|
|
1491
|
-
|
|
1492
|
-
const existingSkills = metadataService.getDeployedSkills(toolName);
|
|
1576
|
+
const deployments = scanner.scanToolDeployment(toolName, config);
|
|
1577
|
+
const mode = deployments.length > 0 && deployments[0].mode ? deployments[0].mode : "all";
|
|
1578
|
+
const existingSkills = scanner.getDeployedSkills(toolName);
|
|
1493
1579
|
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]);
|
|
1580
|
+
if (alreadyExists) {
|
|
1581
|
+
console.log(` \xB7 ${config.displayName} (already deployed)`);
|
|
1582
|
+
continue;
|
|
1501
1583
|
}
|
|
1584
|
+
deployer.deploySkill(skill, config, deployMode, mode);
|
|
1502
1585
|
console.log(
|
|
1503
1586
|
` \u2713 ${config.displayName} (${deployMode === "link" ? "linked" : "copied"})`
|
|
1504
1587
|
);
|
|
@@ -1511,9 +1594,10 @@ var addCommand = new Command5("add").description("Add a skill to the project").a
|
|
|
1511
1594
|
// src/commands/remove.ts
|
|
1512
1595
|
import { Command as Command6 } from "commander";
|
|
1513
1596
|
async function executeRemove(skillName, options) {
|
|
1514
|
-
const
|
|
1597
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1515
1598
|
const deployer = new Deployer(process.cwd());
|
|
1516
|
-
|
|
1599
|
+
const configuredTools = scanner.getConfiguredTools();
|
|
1600
|
+
if (configuredTools.length === 0) {
|
|
1517
1601
|
console.log("No skills deployed in current project.");
|
|
1518
1602
|
process.exit(1);
|
|
1519
1603
|
}
|
|
@@ -1525,22 +1609,21 @@ async function executeRemove(skillName, options) {
|
|
|
1525
1609
|
}
|
|
1526
1610
|
targetTools = [options.tool];
|
|
1527
1611
|
} else {
|
|
1528
|
-
targetTools =
|
|
1612
|
+
targetTools = configuredTools;
|
|
1529
1613
|
}
|
|
1530
1614
|
console.log(`Removing ${skillName}...`);
|
|
1531
1615
|
let removed = false;
|
|
1532
1616
|
for (const toolName of targetTools) {
|
|
1533
1617
|
const config = TOOL_CONFIGS[toolName];
|
|
1534
|
-
const
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
removed = true;
|
|
1618
|
+
const deployments = scanner.scanToolDeployment(toolName, config);
|
|
1619
|
+
for (const deployment of deployments) {
|
|
1620
|
+
const skillToRemove = deployment.skills.find((s) => s.name === skillName);
|
|
1621
|
+
if (!skillToRemove) continue;
|
|
1622
|
+
const mode = deployment.mode || "all";
|
|
1623
|
+
deployer.removeSkill(skillName, config, mode);
|
|
1624
|
+
console.log(` \u2713 Removed from ${config.displayName}`);
|
|
1625
|
+
removed = true;
|
|
1626
|
+
}
|
|
1544
1627
|
}
|
|
1545
1628
|
if (!removed) {
|
|
1546
1629
|
console.log(`Skill '${skillName}' not found in any configured tool`);
|
|
@@ -1554,32 +1637,39 @@ var removeCommand = new Command6("remove").description("Remove a skill from the
|
|
|
1554
1637
|
import { Command as Command7 } from "commander";
|
|
1555
1638
|
import { join as join10 } from "path";
|
|
1556
1639
|
async function executeSync() {
|
|
1557
|
-
const
|
|
1640
|
+
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1558
1641
|
const deployer = new Deployer(process.cwd());
|
|
1559
|
-
|
|
1642
|
+
const skillsService = new SkillsService(SKILLS_MANAGER_DIR);
|
|
1643
|
+
const deployments = scanner.scanAllTools();
|
|
1644
|
+
if (deployments.length === 0) {
|
|
1560
1645
|
console.log("No skills deployed in current project.");
|
|
1561
1646
|
process.exit(1);
|
|
1562
1647
|
}
|
|
1563
1648
|
console.log("Checking deployed skills...\n");
|
|
1564
|
-
const configuredTools = metadataService.getConfiguredTools();
|
|
1565
1649
|
let updatedCount = 0;
|
|
1566
1650
|
let removedCount = 0;
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
const
|
|
1570
|
-
const deployment = metadataService.getToolDeployment(toolName);
|
|
1571
|
-
if (!deployment) continue;
|
|
1651
|
+
for (const deployment of deployments) {
|
|
1652
|
+
const config = TOOL_CONFIGS[deployment.toolName];
|
|
1653
|
+
const mode = deployment.mode || "all";
|
|
1572
1654
|
console.log(`${config.displayName} (${deployment.targetDir}/):`);
|
|
1573
1655
|
for (const skill of deployment.skills) {
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1656
|
+
if (skill.conflict) {
|
|
1657
|
+
console.log(` \u26A0 ${skill.name}: conflict (skipped)`);
|
|
1658
|
+
continue;
|
|
1659
|
+
}
|
|
1660
|
+
const deployedPath = skill.path;
|
|
1661
|
+
let sourcePath = null;
|
|
1662
|
+
if (skill.source !== "unknown") {
|
|
1663
|
+
const sourceSkill = skillsService.getSkillByName(skill.name);
|
|
1664
|
+
if (sourceSkill) {
|
|
1665
|
+
sourcePath = sourceSkill.path;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
if (!sourcePath || !fileExists(sourcePath)) {
|
|
1669
|
+
console.log(` \u2717 ${skill.name}: orphaned (source not found)`);
|
|
1578
1670
|
const action = await promptOrphanAction(skill.name);
|
|
1579
1671
|
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);
|
|
1672
|
+
deployer.removeSkill(skill.name, config, mode);
|
|
1583
1673
|
console.log(` \u2713 Removed ${skill.name}`);
|
|
1584
1674
|
removedCount++;
|
|
1585
1675
|
}
|
|
@@ -1610,7 +1700,7 @@ async function executeSync() {
|
|
|
1610
1700
|
{ name: skill.name, description: "", path: sourcePath, source: skill.source },
|
|
1611
1701
|
config,
|
|
1612
1702
|
"copy",
|
|
1613
|
-
|
|
1703
|
+
mode
|
|
1614
1704
|
);
|
|
1615
1705
|
console.log(` \u2713 Updated ${skill.name}`);
|
|
1616
1706
|
updatedCount++;
|
|
@@ -1620,7 +1710,7 @@ async function executeSync() {
|
|
|
1620
1710
|
{ name: skill.name, description: "", path: sourcePath, source: skill.source },
|
|
1621
1711
|
config,
|
|
1622
1712
|
"copy",
|
|
1623
|
-
|
|
1713
|
+
mode
|
|
1624
1714
|
);
|
|
1625
1715
|
console.log(` \u2713 Updated ${skill.name}`);
|
|
1626
1716
|
updatedCount++;
|
|
@@ -1634,7 +1724,7 @@ async function executeSync() {
|
|
|
1634
1724
|
console.log();
|
|
1635
1725
|
}
|
|
1636
1726
|
console.log(
|
|
1637
|
-
`Sync complete: ${updatedCount} updated, ${removedCount} removed
|
|
1727
|
+
`Sync complete: ${updatedCount} updated, ${removedCount} removed`
|
|
1638
1728
|
);
|
|
1639
1729
|
}
|
|
1640
1730
|
var syncCommand = new Command7("sync").description("Sync and verify deployed skills").action(async () => {
|