skillsmgr 0.3.0 → 0.4.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 +12 -0
- package/README.zh-CN.md +12 -0
- package/dist/index.js +272 -55
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -124,6 +124,18 @@ Sync and verify deployed skills.
|
|
|
124
124
|
npx skillsmgr sync
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
### `npx skillsmgr update`
|
|
128
|
+
|
|
129
|
+
Update installed skills to latest version from remote.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Update all installed sources
|
|
133
|
+
npx skillsmgr update
|
|
134
|
+
|
|
135
|
+
# Update specific source
|
|
136
|
+
npx skillsmgr update anthropic
|
|
137
|
+
```
|
|
138
|
+
|
|
127
139
|
## Directory Structure
|
|
128
140
|
|
|
129
141
|
```
|
package/README.zh-CN.md
CHANGED
|
@@ -124,6 +124,18 @@ npx skillsmgr remove code-review --tool claude-code
|
|
|
124
124
|
npx skillsmgr sync
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
### `npx skillsmgr update`
|
|
128
|
+
|
|
129
|
+
从远程更新已安装的 skills 到最新版本。
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# 更新所有已安装的源
|
|
133
|
+
npx skillsmgr update
|
|
134
|
+
|
|
135
|
+
# 更新特定源
|
|
136
|
+
npx skillsmgr update anthropic
|
|
137
|
+
```
|
|
138
|
+
|
|
127
139
|
## 目录结构
|
|
128
140
|
|
|
129
141
|
```
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command9 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/setup.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -68,6 +68,10 @@ function readSymlinkTarget(path) {
|
|
|
68
68
|
function readFileContent(path) {
|
|
69
69
|
return readFileSync(path, "utf-8");
|
|
70
70
|
}
|
|
71
|
+
function writeFile(path, content) {
|
|
72
|
+
ensureDir(dirname(path));
|
|
73
|
+
writeFileSync(path, content, "utf-8");
|
|
74
|
+
}
|
|
71
75
|
function fileExists(path) {
|
|
72
76
|
return existsSync(path);
|
|
73
77
|
}
|
|
@@ -137,7 +141,7 @@ var setupCommand = new Command("setup").description("Initialize ~/.skills-manage
|
|
|
137
141
|
|
|
138
142
|
// src/commands/install.ts
|
|
139
143
|
import { Command as Command2 } from "commander";
|
|
140
|
-
import { join as
|
|
144
|
+
import { join as join7 } from "path";
|
|
141
145
|
|
|
142
146
|
// src/services/git.ts
|
|
143
147
|
import { execSync } from "child_process";
|
|
@@ -365,6 +369,52 @@ var GitHubService = class {
|
|
|
365
369
|
}
|
|
366
370
|
};
|
|
367
371
|
|
|
372
|
+
// src/services/sources.ts
|
|
373
|
+
import { join as join6 } from "path";
|
|
374
|
+
var SOURCES_FILE = join6(SKILLS_MANAGER_DIR, "sources.json");
|
|
375
|
+
var SourcesService = class {
|
|
376
|
+
load() {
|
|
377
|
+
if (!fileExists(SOURCES_FILE)) {
|
|
378
|
+
return { version: "1.0", sources: {} };
|
|
379
|
+
}
|
|
380
|
+
const content = readFileContent(SOURCES_FILE);
|
|
381
|
+
return JSON.parse(content);
|
|
382
|
+
}
|
|
383
|
+
save(data) {
|
|
384
|
+
writeFile(SOURCES_FILE, JSON.stringify(data, null, 2));
|
|
385
|
+
}
|
|
386
|
+
addSource(key, info) {
|
|
387
|
+
const data = this.load();
|
|
388
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
389
|
+
data.sources[key] = {
|
|
390
|
+
...info,
|
|
391
|
+
installedAt: data.sources[key]?.installedAt || now,
|
|
392
|
+
updatedAt: now
|
|
393
|
+
};
|
|
394
|
+
this.save(data);
|
|
395
|
+
}
|
|
396
|
+
getSource(key) {
|
|
397
|
+
const data = this.load();
|
|
398
|
+
return data.sources[key];
|
|
399
|
+
}
|
|
400
|
+
getAllSources() {
|
|
401
|
+
const data = this.load();
|
|
402
|
+
return data.sources;
|
|
403
|
+
}
|
|
404
|
+
removeSource(key) {
|
|
405
|
+
const data = this.load();
|
|
406
|
+
delete data.sources[key];
|
|
407
|
+
this.save(data);
|
|
408
|
+
}
|
|
409
|
+
updateTimestamp(key) {
|
|
410
|
+
const data = this.load();
|
|
411
|
+
if (data.sources[key]) {
|
|
412
|
+
data.sources[key].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
413
|
+
this.save(data);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
|
|
368
418
|
// src/utils/prompts.ts
|
|
369
419
|
import inquirer from "inquirer";
|
|
370
420
|
|
|
@@ -875,6 +925,7 @@ var ProgressBar = class {
|
|
|
875
925
|
};
|
|
876
926
|
|
|
877
927
|
// src/commands/install.ts
|
|
928
|
+
var sourcesService = new SourcesService();
|
|
878
929
|
function parseSkillDescription(content) {
|
|
879
930
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
880
931
|
if (!frontmatterMatch) {
|
|
@@ -884,16 +935,16 @@ function parseSkillDescription(content) {
|
|
|
884
935
|
return descMatch ? descMatch[1].trim() : "";
|
|
885
936
|
}
|
|
886
937
|
async function installFromAnthropic(options) {
|
|
887
|
-
const
|
|
938
|
+
const githubService2 = new GitHubService();
|
|
888
939
|
const owner = "anthropics";
|
|
889
940
|
const repo = "skills";
|
|
890
941
|
console.log("Fetching available skills from anthropic/skills...");
|
|
891
|
-
const skillsList = await
|
|
942
|
+
const skillsList = await githubService2.listSkills(owner, repo, "skills");
|
|
892
943
|
if (skillsList.length === 0) {
|
|
893
944
|
console.log("No skills found in repository");
|
|
894
945
|
return;
|
|
895
946
|
}
|
|
896
|
-
const defaultBranch = await
|
|
947
|
+
const defaultBranch = await githubService2.getDefaultBranch(owner, repo);
|
|
897
948
|
const skills = [];
|
|
898
949
|
const progress = new ProgressBar(skillsList.length, "Fetching skill info");
|
|
899
950
|
progress.start();
|
|
@@ -932,19 +983,24 @@ async function installFromAnthropic(options) {
|
|
|
932
983
|
}
|
|
933
984
|
console.log(`
|
|
934
985
|
Downloading ${selectedSkills.length} skills...`);
|
|
935
|
-
const targetBase =
|
|
986
|
+
const targetBase = join7(SKILLS_MANAGER_DIR, "official", "anthropic");
|
|
936
987
|
for (const skill of selectedSkills) {
|
|
937
|
-
const targetDir =
|
|
988
|
+
const targetDir = join7(targetBase, skill.name);
|
|
938
989
|
process.stdout.write(` ${skill.name}...`);
|
|
939
|
-
await
|
|
990
|
+
await githubService2.downloadSkill(owner, repo, skill.path, targetDir);
|
|
940
991
|
console.log(" \u2713");
|
|
941
992
|
}
|
|
942
993
|
console.log(`
|
|
943
994
|
\u2713 Installed ${selectedSkills.length} skills to ${targetBase}`);
|
|
995
|
+
sourcesService.addSource("official/anthropic", {
|
|
996
|
+
url: "https://github.com/anthropics/skills",
|
|
997
|
+
type: "official",
|
|
998
|
+
repoName: "anthropic"
|
|
999
|
+
});
|
|
944
1000
|
}
|
|
945
1001
|
async function installFromGitHubUrl(url, options) {
|
|
946
|
-
const
|
|
947
|
-
const parsed =
|
|
1002
|
+
const githubService2 = new GitHubService();
|
|
1003
|
+
const parsed = githubService2.parseGitHubUrl(url);
|
|
948
1004
|
if (!parsed) {
|
|
949
1005
|
return false;
|
|
950
1006
|
}
|
|
@@ -952,9 +1008,9 @@ async function installFromGitHubUrl(url, options) {
|
|
|
952
1008
|
const isAnthropic = owner === "anthropics" && repo === "skills";
|
|
953
1009
|
if (path) {
|
|
954
1010
|
const skillName = path.split("/").pop() || path;
|
|
955
|
-
const targetDir =
|
|
1011
|
+
const targetDir = githubService2.getTargetDir(owner, repo, skillName, options.custom);
|
|
956
1012
|
console.log(`Downloading ${skillName}...`);
|
|
957
|
-
await
|
|
1013
|
+
await githubService2.downloadSkill(owner, repo, path, targetDir);
|
|
958
1014
|
console.log(`\u2713 Installed ${skillName} to ${targetDir}`);
|
|
959
1015
|
return true;
|
|
960
1016
|
}
|
|
@@ -963,7 +1019,7 @@ async function installFromGitHubUrl(url, options) {
|
|
|
963
1019
|
const skillsPaths = ["skills", ".", "src/skills"];
|
|
964
1020
|
for (const skillsPath of skillsPaths) {
|
|
965
1021
|
try {
|
|
966
|
-
skillsList = await
|
|
1022
|
+
skillsList = await githubService2.listSkills(owner, repo, skillsPath);
|
|
967
1023
|
if (skillsList.length > 0) break;
|
|
968
1024
|
} catch {
|
|
969
1025
|
continue;
|
|
@@ -972,7 +1028,7 @@ async function installFromGitHubUrl(url, options) {
|
|
|
972
1028
|
if (skillsList.length === 0) {
|
|
973
1029
|
return false;
|
|
974
1030
|
}
|
|
975
|
-
const defaultBranch = await
|
|
1031
|
+
const defaultBranch = await githubService2.getDefaultBranch(owner, repo);
|
|
976
1032
|
const skills = [];
|
|
977
1033
|
const progress = new ProgressBar(skillsList.length, "Fetching skill info");
|
|
978
1034
|
progress.start();
|
|
@@ -1009,20 +1065,26 @@ async function installFromGitHubUrl(url, options) {
|
|
|
1009
1065
|
Downloading ${selectedSkills.length} skills...`);
|
|
1010
1066
|
let targetBase;
|
|
1011
1067
|
if (isAnthropic) {
|
|
1012
|
-
targetBase =
|
|
1068
|
+
targetBase = join7(SKILLS_MANAGER_DIR, "official", "anthropic");
|
|
1013
1069
|
} else if (options.custom) {
|
|
1014
|
-
targetBase =
|
|
1070
|
+
targetBase = join7(SKILLS_MANAGER_DIR, "custom", repo);
|
|
1015
1071
|
} else {
|
|
1016
|
-
targetBase =
|
|
1072
|
+
targetBase = join7(SKILLS_MANAGER_DIR, "community", repo);
|
|
1017
1073
|
}
|
|
1018
1074
|
for (const skill of selectedSkills) {
|
|
1019
|
-
const targetDir =
|
|
1075
|
+
const targetDir = join7(targetBase, skill.name);
|
|
1020
1076
|
process.stdout.write(` ${skill.name}...`);
|
|
1021
|
-
await
|
|
1077
|
+
await githubService2.downloadSkill(owner, repo, skill.path, targetDir);
|
|
1022
1078
|
console.log(" \u2713");
|
|
1023
1079
|
}
|
|
1024
1080
|
console.log(`
|
|
1025
1081
|
\u2713 Installed ${selectedSkills.length} skills to ${targetBase}`);
|
|
1082
|
+
const sourceKey = isAnthropic ? "official/anthropic" : options.custom ? `custom/${repo}` : `community/${repo}`;
|
|
1083
|
+
sourcesService.addSource(sourceKey, {
|
|
1084
|
+
url: `https://github.com/${owner}/${repo}`,
|
|
1085
|
+
type: isAnthropic ? "official" : options.custom ? "custom" : "community",
|
|
1086
|
+
repoName: repo
|
|
1087
|
+
});
|
|
1026
1088
|
return true;
|
|
1027
1089
|
}
|
|
1028
1090
|
async function installViaGitClone(source, options) {
|
|
@@ -1040,7 +1102,7 @@ async function installViaGitClone(source, options) {
|
|
|
1040
1102
|
const repoPath = gitService.clone(source, options.custom || false);
|
|
1041
1103
|
let skillsRoot = repoPath;
|
|
1042
1104
|
if (source === "anthropic") {
|
|
1043
|
-
const skillsSubdir =
|
|
1105
|
+
const skillsSubdir = join7(repoPath, "skills");
|
|
1044
1106
|
if (fileExists(skillsSubdir)) {
|
|
1045
1107
|
skillsRoot = skillsSubdir;
|
|
1046
1108
|
}
|
|
@@ -1048,7 +1110,7 @@ async function installViaGitClone(source, options) {
|
|
|
1048
1110
|
const skillDirs = getDirectoriesInDir(skillsRoot);
|
|
1049
1111
|
const skills = [];
|
|
1050
1112
|
for (const dir of skillDirs) {
|
|
1051
|
-
const skillMdPath =
|
|
1113
|
+
const skillMdPath = join7(dir.path, "SKILL.md");
|
|
1052
1114
|
if (fileExists(skillMdPath)) {
|
|
1053
1115
|
const content = readFileContent(skillMdPath);
|
|
1054
1116
|
const description = parseSkillDescription(content);
|
|
@@ -1067,6 +1129,7 @@ async function installViaGitClone(source, options) {
|
|
|
1067
1129
|
`);
|
|
1068
1130
|
if (options.all) {
|
|
1069
1131
|
console.log(`\u2713 Installed ${skills.length} skills to ${repoPath}`);
|
|
1132
|
+
saveGitCloneSource(source, repoPath, options);
|
|
1070
1133
|
return;
|
|
1071
1134
|
}
|
|
1072
1135
|
const selectedNames = await promptSkillsToInstall(skills);
|
|
@@ -1081,6 +1144,33 @@ async function installViaGitClone(source, options) {
|
|
|
1081
1144
|
}
|
|
1082
1145
|
console.log(`
|
|
1083
1146
|
\u2713 Installed ${selectedNames.length} skills to ${repoPath}`);
|
|
1147
|
+
saveGitCloneSource(source, repoPath, options);
|
|
1148
|
+
}
|
|
1149
|
+
function saveGitCloneSource(source, repoPath, options) {
|
|
1150
|
+
const repoName = repoPath.split("/").pop() || source;
|
|
1151
|
+
let type;
|
|
1152
|
+
let sourceKey;
|
|
1153
|
+
if (source === "anthropic" || repoPath.includes("/official/")) {
|
|
1154
|
+
type = "official";
|
|
1155
|
+
sourceKey = "official/anthropic";
|
|
1156
|
+
} else if (options.custom || repoPath.includes("/custom/")) {
|
|
1157
|
+
type = "custom";
|
|
1158
|
+
sourceKey = `custom/${repoName}`;
|
|
1159
|
+
} else {
|
|
1160
|
+
type = "community";
|
|
1161
|
+
sourceKey = `community/${repoName}`;
|
|
1162
|
+
}
|
|
1163
|
+
let url = source;
|
|
1164
|
+
if (source === "anthropic") {
|
|
1165
|
+
url = "https://github.com/anthropics/skills";
|
|
1166
|
+
} else if (!source.startsWith("http")) {
|
|
1167
|
+
url = `https://github.com/${source}`;
|
|
1168
|
+
}
|
|
1169
|
+
sourcesService.addSource(sourceKey, {
|
|
1170
|
+
url,
|
|
1171
|
+
type,
|
|
1172
|
+
repoName
|
|
1173
|
+
});
|
|
1084
1174
|
}
|
|
1085
1175
|
async function executeInstall(source, options) {
|
|
1086
1176
|
if (!fileExists(SKILLS_MANAGER_DIR)) {
|
|
@@ -1109,11 +1199,125 @@ var installCommand = new Command2("install").description("Download skills from a
|
|
|
1109
1199
|
await executeInstall(source, options);
|
|
1110
1200
|
});
|
|
1111
1201
|
|
|
1112
|
-
// src/commands/
|
|
1202
|
+
// src/commands/update.ts
|
|
1113
1203
|
import { Command as Command3 } from "commander";
|
|
1204
|
+
import { join as join8 } from "path";
|
|
1205
|
+
var sourcesService2 = new SourcesService();
|
|
1206
|
+
var githubService = new GitHubService();
|
|
1207
|
+
async function updateSource(key, info) {
|
|
1208
|
+
const parsed = githubService.parseGitHubUrl(info.url);
|
|
1209
|
+
if (!parsed) {
|
|
1210
|
+
console.log(` \u26A0 Cannot parse URL: ${info.url}`);
|
|
1211
|
+
return 0;
|
|
1212
|
+
}
|
|
1213
|
+
const { owner, repo } = parsed;
|
|
1214
|
+
let skillsList = [];
|
|
1215
|
+
const skillsPaths = ["skills", ".", "src/skills"];
|
|
1216
|
+
for (const skillsPath of skillsPaths) {
|
|
1217
|
+
try {
|
|
1218
|
+
skillsList = await githubService.listSkills(owner, repo, skillsPath);
|
|
1219
|
+
if (skillsList.length > 0) break;
|
|
1220
|
+
} catch {
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
if (skillsList.length === 0) {
|
|
1225
|
+
console.log(` \u26A0 No skills found in ${owner}/${repo}`);
|
|
1226
|
+
return 0;
|
|
1227
|
+
}
|
|
1228
|
+
const defaultBranch = await githubService.getDefaultBranch(owner, repo);
|
|
1229
|
+
const skills = [];
|
|
1230
|
+
const progress = new ProgressBar(skillsList.length, "Checking skills");
|
|
1231
|
+
progress.start();
|
|
1232
|
+
for (const skill of skillsList) {
|
|
1233
|
+
try {
|
|
1234
|
+
const response = await fetch(
|
|
1235
|
+
`https://raw.githubusercontent.com/${owner}/${repo}/${defaultBranch}/${skill.path}/SKILL.md`
|
|
1236
|
+
);
|
|
1237
|
+
if (response.ok) {
|
|
1238
|
+
skills.push(skill);
|
|
1239
|
+
}
|
|
1240
|
+
} catch {
|
|
1241
|
+
}
|
|
1242
|
+
progress.tick();
|
|
1243
|
+
}
|
|
1244
|
+
progress.complete();
|
|
1245
|
+
if (skills.length === 0) {
|
|
1246
|
+
return 0;
|
|
1247
|
+
}
|
|
1248
|
+
let targetBase;
|
|
1249
|
+
if (info.type === "official") {
|
|
1250
|
+
targetBase = join8(SKILLS_MANAGER_DIR, "official", info.repoName);
|
|
1251
|
+
} else if (info.type === "custom") {
|
|
1252
|
+
targetBase = join8(SKILLS_MANAGER_DIR, "custom", info.repoName);
|
|
1253
|
+
} else {
|
|
1254
|
+
targetBase = join8(SKILLS_MANAGER_DIR, "community", info.repoName);
|
|
1255
|
+
}
|
|
1256
|
+
let updatedCount = 0;
|
|
1257
|
+
for (const skill of skills) {
|
|
1258
|
+
const targetDir = join8(targetBase, skill.name);
|
|
1259
|
+
try {
|
|
1260
|
+
if (fileExists(targetDir)) {
|
|
1261
|
+
removeDir(targetDir);
|
|
1262
|
+
}
|
|
1263
|
+
await githubService.downloadSkill(owner, repo, skill.path, targetDir);
|
|
1264
|
+
updatedCount++;
|
|
1265
|
+
} catch {
|
|
1266
|
+
console.log(` \u26A0 Failed to update ${skill.name}`);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
sourcesService2.updateTimestamp(key);
|
|
1270
|
+
return updatedCount;
|
|
1271
|
+
}
|
|
1272
|
+
async function executeUpdate(source) {
|
|
1273
|
+
if (!fileExists(SKILLS_MANAGER_DIR)) {
|
|
1274
|
+
console.log("Skills manager not set up. Run: skillsmgr setup");
|
|
1275
|
+
process.exit(1);
|
|
1276
|
+
}
|
|
1277
|
+
const allSources = sourcesService2.getAllSources();
|
|
1278
|
+
if (Object.keys(allSources).length === 0) {
|
|
1279
|
+
console.log("No installed sources found.");
|
|
1280
|
+
console.log("\nRun: skillsmgr install anthropic");
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
if (source) {
|
|
1284
|
+
const matchingKey = Object.keys(allSources).find(
|
|
1285
|
+
(k) => k === source || k.endsWith(`/${source}`) || allSources[k].repoName === source
|
|
1286
|
+
);
|
|
1287
|
+
if (!matchingKey) {
|
|
1288
|
+
console.log(`Source '${source}' not found.`);
|
|
1289
|
+
console.log("\nInstalled sources:");
|
|
1290
|
+
for (const key of Object.keys(allSources)) {
|
|
1291
|
+
console.log(` ${key}`);
|
|
1292
|
+
}
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
console.log(`Updating ${matchingKey}...`);
|
|
1296
|
+
const count = await updateSource(matchingKey, allSources[matchingKey]);
|
|
1297
|
+
console.log(`
|
|
1298
|
+
\u2713 Updated ${count} skills from ${matchingKey}`);
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
console.log("Updating all installed sources...\n");
|
|
1302
|
+
let totalUpdated = 0;
|
|
1303
|
+
for (const [key, info] of Object.entries(allSources)) {
|
|
1304
|
+
console.log(`${key}:`);
|
|
1305
|
+
const count = await updateSource(key, info);
|
|
1306
|
+
console.log(` \u2713 ${count} skills updated
|
|
1307
|
+
`);
|
|
1308
|
+
totalUpdated += count;
|
|
1309
|
+
}
|
|
1310
|
+
console.log(`Done! Updated ${totalUpdated} skills from ${Object.keys(allSources).length} sources.`);
|
|
1311
|
+
}
|
|
1312
|
+
var updateCommand = new Command3("update").description("Update installed skills to latest version").argument("[source]", 'Specific source to update (e.g., "anthropic")').action(async (source) => {
|
|
1313
|
+
await executeUpdate(source);
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
// src/commands/list.ts
|
|
1317
|
+
import { Command as Command4 } from "commander";
|
|
1114
1318
|
|
|
1115
1319
|
// src/services/skills.ts
|
|
1116
|
-
import { join as
|
|
1320
|
+
import { join as join9 } from "path";
|
|
1117
1321
|
var SkillsService = class {
|
|
1118
1322
|
constructor(skillsDir) {
|
|
1119
1323
|
this.skillsDir = skillsDir;
|
|
@@ -1121,14 +1325,14 @@ var SkillsService = class {
|
|
|
1121
1325
|
getAllSkills() {
|
|
1122
1326
|
const skills = [];
|
|
1123
1327
|
for (const source of SKILL_SOURCES) {
|
|
1124
|
-
const sourceDir =
|
|
1328
|
+
const sourceDir = join9(this.skillsDir, source);
|
|
1125
1329
|
const sourceSkills = this.getSkillsFromSource(sourceDir, source);
|
|
1126
1330
|
skills.push(...sourceSkills);
|
|
1127
1331
|
}
|
|
1128
1332
|
return skills;
|
|
1129
1333
|
}
|
|
1130
1334
|
getSkillsBySource(source) {
|
|
1131
|
-
const sourceDir =
|
|
1335
|
+
const sourceDir = join9(this.skillsDir, source);
|
|
1132
1336
|
return this.getSkillsFromSource(sourceDir, source);
|
|
1133
1337
|
}
|
|
1134
1338
|
getSkillByName(name) {
|
|
@@ -1159,7 +1363,7 @@ var SkillsService = class {
|
|
|
1159
1363
|
} else {
|
|
1160
1364
|
const repoDirs = getDirectoriesInDir(sourceDir);
|
|
1161
1365
|
for (const repoDir of repoDirs) {
|
|
1162
|
-
const skillsSubdir =
|
|
1366
|
+
const skillsSubdir = join9(repoDir.path, "skills");
|
|
1163
1367
|
const searchDir = fileExists(skillsSubdir) ? skillsSubdir : repoDir.path;
|
|
1164
1368
|
const skillDirs = getDirectoriesInDir(searchDir);
|
|
1165
1369
|
for (const skillDir of skillDirs) {
|
|
@@ -1174,7 +1378,7 @@ var SkillsService = class {
|
|
|
1174
1378
|
return skills;
|
|
1175
1379
|
}
|
|
1176
1380
|
loadSkill(skillPath, source) {
|
|
1177
|
-
const skillMdPath =
|
|
1381
|
+
const skillMdPath = join9(skillPath, "SKILL.md");
|
|
1178
1382
|
if (!fileExists(skillMdPath)) {
|
|
1179
1383
|
return void 0;
|
|
1180
1384
|
}
|
|
@@ -1203,7 +1407,7 @@ var SkillsService = class {
|
|
|
1203
1407
|
};
|
|
1204
1408
|
|
|
1205
1409
|
// src/services/scanner.ts
|
|
1206
|
-
import { join as
|
|
1410
|
+
import { join as join10 } from "path";
|
|
1207
1411
|
import { readdirSync as readdirSync2 } from "fs";
|
|
1208
1412
|
var DeploymentScanner = class {
|
|
1209
1413
|
constructor(projectDir, skillsManagerDir = SKILLS_MANAGER_DIR) {
|
|
@@ -1223,7 +1427,7 @@ var DeploymentScanner = class {
|
|
|
1223
1427
|
}
|
|
1224
1428
|
scanToolDeployment(toolName, config) {
|
|
1225
1429
|
const deployments = [];
|
|
1226
|
-
const baseDir =
|
|
1430
|
+
const baseDir = join10(this.projectDir, config.skillsDir);
|
|
1227
1431
|
const baseDeployment = this.scanDirectory(toolName, baseDir, config.skillsDir);
|
|
1228
1432
|
if (baseDeployment.skills.length > 0) {
|
|
1229
1433
|
deployments.push(baseDeployment);
|
|
@@ -1231,7 +1435,7 @@ var DeploymentScanner = class {
|
|
|
1231
1435
|
if (config.supportsModeSpecific && config.availableModes) {
|
|
1232
1436
|
for (const mode of config.availableModes) {
|
|
1233
1437
|
const modeDir = getTargetDir(config, mode);
|
|
1234
|
-
const fullModeDir =
|
|
1438
|
+
const fullModeDir = join10(this.projectDir, modeDir);
|
|
1235
1439
|
const modeDeployment = this.scanDirectory(toolName, fullModeDir, modeDir, mode);
|
|
1236
1440
|
if (modeDeployment.skills.length > 0) {
|
|
1237
1441
|
deployments.push(modeDeployment);
|
|
@@ -1276,7 +1480,7 @@ var DeploymentScanner = class {
|
|
|
1276
1480
|
try {
|
|
1277
1481
|
const entries = readdirSync2(fullPath, { withFileTypes: true });
|
|
1278
1482
|
for (const entry of entries) {
|
|
1279
|
-
const skillPath =
|
|
1483
|
+
const skillPath = join10(fullPath, entry.name);
|
|
1280
1484
|
const scanned = this.scanSkill(skillPath, entry.name);
|
|
1281
1485
|
if (scanned) {
|
|
1282
1486
|
deployment.skills.push(scanned);
|
|
@@ -1287,7 +1491,7 @@ var DeploymentScanner = class {
|
|
|
1287
1491
|
return deployment;
|
|
1288
1492
|
}
|
|
1289
1493
|
scanSkill(skillPath, name) {
|
|
1290
|
-
const skillMdPath =
|
|
1494
|
+
const skillMdPath = join10(skillPath, "SKILL.md");
|
|
1291
1495
|
if (!fileExists(skillMdPath)) {
|
|
1292
1496
|
return null;
|
|
1293
1497
|
}
|
|
@@ -1311,12 +1515,13 @@ var DeploymentScanner = class {
|
|
|
1311
1515
|
};
|
|
1312
1516
|
}
|
|
1313
1517
|
scanCopiedSkill(skillPath, name) {
|
|
1314
|
-
const source = this.findSourceByName(name);
|
|
1518
|
+
const { source, conflict } = this.findSourceByName(name);
|
|
1315
1519
|
return {
|
|
1316
1520
|
name,
|
|
1317
1521
|
source: source || "unknown",
|
|
1318
1522
|
deployMode: "copy",
|
|
1319
|
-
path: skillPath
|
|
1523
|
+
path: skillPath,
|
|
1524
|
+
conflict
|
|
1320
1525
|
};
|
|
1321
1526
|
}
|
|
1322
1527
|
extractSourceFromPath(linkTarget) {
|
|
@@ -1337,9 +1542,12 @@ var DeploymentScanner = class {
|
|
|
1337
1542
|
findSourceByName(skillName) {
|
|
1338
1543
|
const matches = this.skillsService.findSkillsByName(skillName);
|
|
1339
1544
|
if (matches.length === 0) {
|
|
1340
|
-
return null;
|
|
1545
|
+
return { source: null, conflict: false };
|
|
1546
|
+
}
|
|
1547
|
+
if (matches.length > 1) {
|
|
1548
|
+
return { source: null, conflict: true };
|
|
1341
1549
|
}
|
|
1342
|
-
return matches[0].source;
|
|
1550
|
+
return { source: matches[0].source, conflict: false };
|
|
1343
1551
|
}
|
|
1344
1552
|
};
|
|
1345
1553
|
|
|
@@ -1395,20 +1603,24 @@ async function listDeployed() {
|
|
|
1395
1603
|
console.log(`${displayName} (${deployment.targetDir}/)${dirSuffix}:`);
|
|
1396
1604
|
for (const skill of deployment.skills) {
|
|
1397
1605
|
const modeStr = skill.deployMode === "link" ? "link" : "copy";
|
|
1398
|
-
|
|
1606
|
+
if (skill.conflict) {
|
|
1607
|
+
console.log(` \u26A0 ${skill.name.padEnd(16)} (${modeStr}) \u2190 conflict`);
|
|
1608
|
+
} else {
|
|
1609
|
+
console.log(` \u25C9 ${skill.name.padEnd(16)} (${modeStr}) \u2190 ${skill.source}`);
|
|
1610
|
+
}
|
|
1399
1611
|
}
|
|
1400
1612
|
console.log();
|
|
1401
1613
|
}
|
|
1402
1614
|
}
|
|
1403
|
-
var listCommand = new
|
|
1615
|
+
var listCommand = new Command4("list").description("List available or deployed skills").option("--deployed", "List deployed skills in current project").action(async (options) => {
|
|
1404
1616
|
await executeList(options);
|
|
1405
1617
|
});
|
|
1406
1618
|
|
|
1407
1619
|
// src/commands/init.ts
|
|
1408
|
-
import { Command as
|
|
1620
|
+
import { Command as Command5 } from "commander";
|
|
1409
1621
|
|
|
1410
1622
|
// src/services/deployer.ts
|
|
1411
|
-
import { join as
|
|
1623
|
+
import { join as join11 } from "path";
|
|
1412
1624
|
import { existsSync as existsSync3, rmSync as rmSync2 } from "fs";
|
|
1413
1625
|
var Deployer = class {
|
|
1414
1626
|
constructor(projectDir) {
|
|
@@ -1416,9 +1628,9 @@ var Deployer = class {
|
|
|
1416
1628
|
}
|
|
1417
1629
|
deploySkill(skill, toolConfig, mode, targetMode) {
|
|
1418
1630
|
const targetDir = getTargetDir(toolConfig, targetMode);
|
|
1419
|
-
const fullTargetDir =
|
|
1631
|
+
const fullTargetDir = join11(this.projectDir, targetDir);
|
|
1420
1632
|
ensureDir(fullTargetDir);
|
|
1421
|
-
const skillTargetPath =
|
|
1633
|
+
const skillTargetPath = join11(fullTargetDir, skill.name);
|
|
1422
1634
|
if (mode === "link") {
|
|
1423
1635
|
linkDir(skill.path, skillTargetPath);
|
|
1424
1636
|
} else {
|
|
@@ -1432,14 +1644,14 @@ var Deployer = class {
|
|
|
1432
1644
|
}
|
|
1433
1645
|
removeSkill(skillName, toolConfig, targetMode) {
|
|
1434
1646
|
const targetDir = getTargetDir(toolConfig, targetMode);
|
|
1435
|
-
const skillPath =
|
|
1647
|
+
const skillPath = join11(this.projectDir, targetDir, skillName);
|
|
1436
1648
|
if (existsSync3(skillPath)) {
|
|
1437
1649
|
rmSync2(skillPath, { recursive: true, force: true });
|
|
1438
1650
|
}
|
|
1439
1651
|
}
|
|
1440
1652
|
getDeployedSkillPath(skillName, toolConfig, targetMode) {
|
|
1441
1653
|
const targetDir = getTargetDir(toolConfig, targetMode);
|
|
1442
|
-
return
|
|
1654
|
+
return join11(this.projectDir, targetDir, skillName);
|
|
1443
1655
|
}
|
|
1444
1656
|
isSkillDeployed(skillName, toolConfig, targetMode) {
|
|
1445
1657
|
const skillPath = this.getDeployedSkillPath(skillName, toolConfig, targetMode);
|
|
@@ -1518,12 +1730,12 @@ async function executeInit(options) {
|
|
|
1518
1730
|
`Done! Deployed ${selectedSkillNames.length} skills to ${selectedTools.length} tool${selectedTools.length > 1 ? "s" : ""}.`
|
|
1519
1731
|
);
|
|
1520
1732
|
}
|
|
1521
|
-
var initCommand = new
|
|
1733
|
+
var initCommand = new Command5("init").description("Deploy skills to current project").option("--copy", "Copy files instead of creating symlinks").action(async (options) => {
|
|
1522
1734
|
await executeInit(options);
|
|
1523
1735
|
});
|
|
1524
1736
|
|
|
1525
1737
|
// src/commands/add.ts
|
|
1526
|
-
import { Command as
|
|
1738
|
+
import { Command as Command6 } from "commander";
|
|
1527
1739
|
async function executeAdd(skillName, options) {
|
|
1528
1740
|
if (!fileExists(SKILLS_MANAGER_DIR)) {
|
|
1529
1741
|
console.log("Skills manager not set up. Run: skillsmgr setup");
|
|
@@ -1579,12 +1791,12 @@ async function executeAdd(skillName, options) {
|
|
|
1579
1791
|
);
|
|
1580
1792
|
}
|
|
1581
1793
|
}
|
|
1582
|
-
var addCommand = new
|
|
1794
|
+
var addCommand = new Command6("add").description("Add a skill to the project").argument("<skill>", "Skill name to add").option("--tool <tool>", "Add to specific tool only").option("--copy", "Copy files instead of creating symlinks").action(async (skill, options) => {
|
|
1583
1795
|
await executeAdd(skill, options);
|
|
1584
1796
|
});
|
|
1585
1797
|
|
|
1586
1798
|
// src/commands/remove.ts
|
|
1587
|
-
import { Command as
|
|
1799
|
+
import { Command as Command7 } from "commander";
|
|
1588
1800
|
async function executeRemove(skillName, options) {
|
|
1589
1801
|
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1590
1802
|
const deployer = new Deployer(process.cwd());
|
|
@@ -1621,13 +1833,13 @@ async function executeRemove(skillName, options) {
|
|
|
1621
1833
|
console.log(`Skill '${skillName}' not found in any configured tool`);
|
|
1622
1834
|
}
|
|
1623
1835
|
}
|
|
1624
|
-
var removeCommand = new
|
|
1836
|
+
var removeCommand = new Command7("remove").description("Remove a skill from the project").argument("<skill>", "Skill name to remove").option("--tool <tool>", "Remove from specific tool only").action(async (skill, options) => {
|
|
1625
1837
|
await executeRemove(skill, options);
|
|
1626
1838
|
});
|
|
1627
1839
|
|
|
1628
1840
|
// src/commands/sync.ts
|
|
1629
|
-
import { Command as
|
|
1630
|
-
import { join as
|
|
1841
|
+
import { Command as Command8 } from "commander";
|
|
1842
|
+
import { join as join12 } from "path";
|
|
1631
1843
|
async function executeSync() {
|
|
1632
1844
|
const scanner = new DeploymentScanner(process.cwd(), SKILLS_MANAGER_DIR);
|
|
1633
1845
|
const deployer = new Deployer(process.cwd());
|
|
@@ -1645,6 +1857,10 @@ async function executeSync() {
|
|
|
1645
1857
|
const mode = deployment.mode || "all";
|
|
1646
1858
|
console.log(`${config.displayName} (${deployment.targetDir}/):`);
|
|
1647
1859
|
for (const skill of deployment.skills) {
|
|
1860
|
+
if (skill.conflict) {
|
|
1861
|
+
console.log(` \u26A0 ${skill.name}: conflict (skipped)`);
|
|
1862
|
+
continue;
|
|
1863
|
+
}
|
|
1648
1864
|
const deployedPath = skill.path;
|
|
1649
1865
|
let sourcePath = null;
|
|
1650
1866
|
if (skill.source !== "unknown") {
|
|
@@ -1668,8 +1884,8 @@ async function executeSync() {
|
|
|
1668
1884
|
continue;
|
|
1669
1885
|
}
|
|
1670
1886
|
if (skill.deployMode === "copy") {
|
|
1671
|
-
const sourceSkillMd =
|
|
1672
|
-
const deployedSkillMd =
|
|
1887
|
+
const sourceSkillMd = join12(sourcePath, "SKILL.md");
|
|
1888
|
+
const deployedSkillMd = join12(deployedPath, "SKILL.md");
|
|
1673
1889
|
if (fileExists(sourceSkillMd) && fileExists(deployedSkillMd)) {
|
|
1674
1890
|
const sourceContent = readFileContent(sourceSkillMd);
|
|
1675
1891
|
const deployedContent = readFileContent(deployedSkillMd);
|
|
@@ -1715,15 +1931,16 @@ async function executeSync() {
|
|
|
1715
1931
|
`Sync complete: ${updatedCount} updated, ${removedCount} removed`
|
|
1716
1932
|
);
|
|
1717
1933
|
}
|
|
1718
|
-
var syncCommand = new
|
|
1934
|
+
var syncCommand = new Command8("sync").description("Sync and verify deployed skills").action(async () => {
|
|
1719
1935
|
await executeSync();
|
|
1720
1936
|
});
|
|
1721
1937
|
|
|
1722
1938
|
// src/index.ts
|
|
1723
|
-
var program = new
|
|
1939
|
+
var program = new Command9();
|
|
1724
1940
|
program.name("skillsmgr").description("Unified skills manager for AI coding tools").version("0.1.0");
|
|
1725
1941
|
program.addCommand(setupCommand);
|
|
1726
1942
|
program.addCommand(installCommand);
|
|
1943
|
+
program.addCommand(updateCommand);
|
|
1727
1944
|
program.addCommand(listCommand);
|
|
1728
1945
|
program.addCommand(initCommand);
|
|
1729
1946
|
program.addCommand(addCommand);
|