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 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 7 different AI tools
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
@@ -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/metadata.ts
1205
+ // src/services/scanner.ts
1203
1206
  import { join as join8 } from "path";
1204
- var MetadataService = class {
1205
- constructor(projectDir) {
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.metadataPath = join8(projectDir, METADATA_FILENAME);
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
- metadataPath;
1210
- load() {
1211
- if (!fileExists(this.metadataPath)) {
1212
- return { version: "1.0", tools: {} };
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
- const content = readFileContent(this.metadataPath);
1215
- return JSON.parse(content);
1252
+ return Array.from(tools);
1216
1253
  }
1217
- save(metadata) {
1218
- writeFile(this.metadataPath, JSON.stringify(metadata, null, 2));
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
- addDeployment(toolName, targetDir, mode, skills) {
1221
- const metadata = this.load();
1222
- metadata.tools[toolName] = {
1223
- targetDir,
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
- deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
1226
- skills
1271
+ skills: []
1227
1272
  };
1228
- this.save(metadata);
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
- this.save(metadata);
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
- removeDeployment(toolName) {
1239
- const metadata = this.load();
1240
- delete metadata.tools[toolName];
1241
- this.save(metadata);
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
- getDeployedSkills(toolName) {
1244
- const metadata = this.load();
1245
- return metadata.tools[toolName]?.skills || [];
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
- getToolDeployment(toolName) {
1248
- const metadata = this.load();
1249
- return metadata.tools[toolName];
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
- getConfiguredTools() {
1252
- const metadata = this.load();
1253
- return Object.keys(metadata.tools);
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
- hasMetadata() {
1256
- return fileExists(this.metadataPath);
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.padEnd(20)} ${skill.description}`);
1377
+ console.log(` ${skill.name}`);
1292
1378
  }
1293
1379
  console.log();
1294
1380
  }
1295
1381
  }
1296
1382
  async function listDeployed() {
1297
- const metadataService = new MetadataService(process.cwd());
1298
- if (!metadataService.hasMetadata()) {
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 configuredTools = metadataService.getConfiguredTools();
1305
- for (const toolName of configuredTools) {
1306
- const deployment = metadataService.getToolDeployment(toolName);
1307
- if (!deployment) continue;
1308
- const config = TOOL_CONFIGS[toolName];
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 metadataService = new MetadataService(process.cwd());
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 = metadataService.getConfiguredTools();
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 = metadataService.getDeployedSkills(toolName);
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 = metadataService.getDeployedSkills(toolName);
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 metadataService = new MetadataService(process.cwd());
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 = metadataService.getConfiguredTools();
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 deployment = metadataService.getToolDeployment(toolName);
1490
- const mode = deployment?.mode || "all";
1491
- deployer.deploySkill(skill, config, deployMode, mode);
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 (!alreadyExists) {
1495
- const newSkill = {
1496
- name: skill.name,
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 metadataService = new MetadataService(process.cwd());
1589
+ const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
1515
1590
  const deployer = new Deployer(process.cwd());
1516
- if (!metadataService.hasMetadata()) {
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 = metadataService.getConfiguredTools();
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 deployment = metadataService.getToolDeployment(toolName);
1535
- if (!deployment) continue;
1536
- const existingSkills = deployment.skills;
1537
- const skillToRemove = existingSkills.find((s) => s.name === skillName);
1538
- if (!skillToRemove) continue;
1539
- deployer.removeSkill(skillName, config, deployment.mode);
1540
- const remainingSkills = existingSkills.filter((s) => s.name !== skillName);
1541
- metadataService.updateDeployment(toolName, remainingSkills);
1542
- console.log(` \u2713 Removed from ${config.displayName}`);
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 metadataService = new MetadataService(process.cwd());
1632
+ const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
1558
1633
  const deployer = new Deployer(process.cwd());
1559
- if (!metadataService.hasMetadata()) {
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
- let untrackedCount = 0;
1568
- for (const toolName of configuredTools) {
1569
- const config = TOOL_CONFIGS[toolName];
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 = join10(process.cwd(), deployment.targetDir, skill.name);
1575
- const sourcePath = join10(SKILLS_MANAGER_DIR, skill.source, skill.name);
1576
- if (!fileExists(sourcePath)) {
1577
- console.log(` \u2717 ${skill.name}: orphaned (source deleted)`);
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, deployment.mode);
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
- deployment.mode
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
- deployment.mode
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, ${untrackedCount} untracked`
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 () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillsmgr",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Unified skills manager for AI coding tools",
5
5
  "type": "module",
6
6
  "bin": {