ali-skills 0.0.24 → 0.0.25
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.
|
@@ -916,29 +916,32 @@ const agents = {
|
|
|
916
916
|
return existsSync(claudeHome);
|
|
917
917
|
}
|
|
918
918
|
},
|
|
919
|
+
codex: {
|
|
920
|
+
name: "codex",
|
|
921
|
+
displayName: "Codex",
|
|
922
|
+
skillsDir: ".codex/skills",
|
|
923
|
+
globalSkillsDir: join(codexHome, "skills"),
|
|
924
|
+
supportsSharedAgentsDir: true,
|
|
925
|
+
detectInstalled: async () => {
|
|
926
|
+
return existsSync(codexHome) || existsSync("/etc/codex");
|
|
927
|
+
}
|
|
928
|
+
},
|
|
919
929
|
cursor: {
|
|
920
930
|
name: "cursor",
|
|
921
931
|
displayName: "Cursor",
|
|
922
|
-
skillsDir: ".
|
|
932
|
+
skillsDir: ".cursor/skills",
|
|
923
933
|
globalSkillsDir: join(home, ".cursor/skills"),
|
|
934
|
+
supportsSharedAgentsDir: true,
|
|
924
935
|
detectInstalled: async () => {
|
|
925
936
|
return existsSync(join(home, ".cursor"));
|
|
926
937
|
}
|
|
927
938
|
},
|
|
928
|
-
"aone-copilot": {
|
|
929
|
-
name: "aone-copilot",
|
|
930
|
-
displayName: "Aone Copilot",
|
|
931
|
-
skillsDir: ".agents/skills",
|
|
932
|
-
globalSkillsDir: join(configHome, "agents/skills"),
|
|
933
|
-
detectInstalled: async () => {
|
|
934
|
-
return existsSync(join(home, ".aone-copilot"));
|
|
935
|
-
}
|
|
936
|
-
},
|
|
937
939
|
qoder: {
|
|
938
940
|
name: "qoder",
|
|
939
941
|
displayName: "Qoder",
|
|
940
|
-
skillsDir: ".
|
|
941
|
-
globalSkillsDir: join(home, ".
|
|
942
|
+
skillsDir: ".qoder/skills",
|
|
943
|
+
globalSkillsDir: join(home, ".qoder/skills"),
|
|
944
|
+
supportsSharedAgentsDir: true,
|
|
942
945
|
detectInstalled: async () => {
|
|
943
946
|
return existsSync(join(home, ".qoder"));
|
|
944
947
|
}
|
|
@@ -952,94 +955,90 @@ const agents = {
|
|
|
952
955
|
return existsSync(join(home, ".qoderwork"));
|
|
953
956
|
}
|
|
954
957
|
},
|
|
955
|
-
|
|
956
|
-
name: "
|
|
957
|
-
displayName: "
|
|
958
|
-
skillsDir: "
|
|
959
|
-
globalSkillsDir:
|
|
958
|
+
openclaw: {
|
|
959
|
+
name: "openclaw",
|
|
960
|
+
displayName: "OpenClaw",
|
|
961
|
+
skillsDir: "skills",
|
|
962
|
+
globalSkillsDir: getOpenClawGlobalSkillsDir(),
|
|
960
963
|
detectInstalled: async () => {
|
|
961
|
-
return existsSync(join(home, ".
|
|
964
|
+
return existsSync(join(home, ".openclaw")) || existsSync(join(home, ".clawdbot")) || existsSync(join(home, ".moltbot"));
|
|
962
965
|
}
|
|
963
966
|
},
|
|
964
|
-
"
|
|
965
|
-
name: "
|
|
966
|
-
displayName: "
|
|
967
|
-
skillsDir: ".
|
|
968
|
-
globalSkillsDir: join(home, ".
|
|
967
|
+
"aone-copilot": {
|
|
968
|
+
name: "aone-copilot",
|
|
969
|
+
displayName: "Aone Copilot",
|
|
970
|
+
skillsDir: ".aone_copilot/skills",
|
|
971
|
+
globalSkillsDir: join(home, ".aone_copilot/skills"),
|
|
972
|
+
supportsSharedAgentsDir: true,
|
|
969
973
|
detectInstalled: async () => {
|
|
970
|
-
return existsSync(join(home, ".
|
|
974
|
+
return existsSync(join(home, ".aone-copilot"));
|
|
971
975
|
}
|
|
972
976
|
},
|
|
973
977
|
"github-copilot": {
|
|
974
978
|
name: "github-copilot",
|
|
975
979
|
displayName: "GitHub Copilot",
|
|
976
|
-
skillsDir: ".
|
|
980
|
+
skillsDir: ".github/skills",
|
|
977
981
|
globalSkillsDir: join(home, ".copilot/skills"),
|
|
982
|
+
supportsSharedAgentsDir: true,
|
|
978
983
|
detectInstalled: async () => {
|
|
979
984
|
return existsSync(join(home, ".copilot"));
|
|
980
985
|
}
|
|
981
986
|
},
|
|
982
|
-
codex: {
|
|
983
|
-
name: "codex",
|
|
984
|
-
displayName: "Codex",
|
|
985
|
-
skillsDir: ".agents/skills",
|
|
986
|
-
globalSkillsDir: join(codexHome, "skills"),
|
|
987
|
-
detectInstalled: async () => {
|
|
988
|
-
return existsSync(codexHome) || existsSync("/etc/codex");
|
|
989
|
-
}
|
|
990
|
-
},
|
|
991
987
|
opencode: {
|
|
992
988
|
name: "opencode",
|
|
993
989
|
displayName: "OpenCode",
|
|
994
|
-
skillsDir: ".
|
|
990
|
+
skillsDir: ".opencode/skills",
|
|
995
991
|
globalSkillsDir: join(configHome, "opencode/skills"),
|
|
992
|
+
supportsSharedAgentsDir: true,
|
|
996
993
|
detectInstalled: async () => {
|
|
997
994
|
return existsSync(join(configHome, "opencode"));
|
|
998
995
|
}
|
|
999
996
|
},
|
|
1000
|
-
openclaw: {
|
|
1001
|
-
name: "openclaw",
|
|
1002
|
-
displayName: "OpenClaw",
|
|
1003
|
-
skillsDir: "skills",
|
|
1004
|
-
globalSkillsDir: getOpenClawGlobalSkillsDir(),
|
|
1005
|
-
detectInstalled: async () => {
|
|
1006
|
-
return existsSync(join(home, ".openclaw")) || existsSync(join(home, ".clawdbot")) || existsSync(join(home, ".moltbot"));
|
|
1007
|
-
}
|
|
1008
|
-
},
|
|
1009
997
|
"gemini-cli": {
|
|
1010
998
|
name: "gemini-cli",
|
|
1011
999
|
displayName: "Gemini CLI",
|
|
1012
|
-
skillsDir: ".
|
|
1000
|
+
skillsDir: ".gemini/skills",
|
|
1013
1001
|
globalSkillsDir: join(home, ".gemini/skills"),
|
|
1002
|
+
supportsSharedAgentsDir: true,
|
|
1014
1003
|
detectInstalled: async () => {
|
|
1015
1004
|
return existsSync(join(home, ".gemini"));
|
|
1016
1005
|
}
|
|
1017
1006
|
},
|
|
1018
|
-
|
|
1019
|
-
name: "
|
|
1020
|
-
displayName: "
|
|
1021
|
-
skillsDir: ".
|
|
1022
|
-
globalSkillsDir: join(home, ".
|
|
1007
|
+
"qwen-code": {
|
|
1008
|
+
name: "qwen-code",
|
|
1009
|
+
displayName: "Qwen Code",
|
|
1010
|
+
skillsDir: ".qwen/skills",
|
|
1011
|
+
globalSkillsDir: join(home, ".qwen/skills"),
|
|
1023
1012
|
detectInstalled: async () => {
|
|
1024
|
-
return existsSync(join(home, ".
|
|
1013
|
+
return existsSync(join(home, ".qwen"));
|
|
1025
1014
|
}
|
|
1026
1015
|
},
|
|
1027
|
-
|
|
1028
|
-
name: "
|
|
1029
|
-
displayName: "
|
|
1030
|
-
skillsDir: ".
|
|
1031
|
-
globalSkillsDir: join(home, ".
|
|
1016
|
+
"kimi-cli": {
|
|
1017
|
+
name: "kimi-cli",
|
|
1018
|
+
displayName: "Kimi Code CLI",
|
|
1019
|
+
skillsDir: ".kimi/skills",
|
|
1020
|
+
globalSkillsDir: join(home, ".kimi/skills"),
|
|
1021
|
+
supportsSharedAgentsDir: true,
|
|
1032
1022
|
detectInstalled: async () => {
|
|
1033
|
-
return existsSync(join(home, ".
|
|
1023
|
+
return existsSync(join(home, ".kimi"));
|
|
1034
1024
|
}
|
|
1035
1025
|
},
|
|
1036
|
-
|
|
1037
|
-
name: "
|
|
1038
|
-
displayName: "
|
|
1039
|
-
skillsDir: ".
|
|
1040
|
-
globalSkillsDir: join(home, ".
|
|
1026
|
+
"iflow-cli": {
|
|
1027
|
+
name: "iflow-cli",
|
|
1028
|
+
displayName: "iFlow CLI",
|
|
1029
|
+
skillsDir: ".iflow/skills",
|
|
1030
|
+
globalSkillsDir: join(home, ".iflow/skills"),
|
|
1041
1031
|
detectInstalled: async () => {
|
|
1042
|
-
return existsSync(join(
|
|
1032
|
+
return existsSync(join(home, ".iflow"));
|
|
1033
|
+
}
|
|
1034
|
+
},
|
|
1035
|
+
antigravity: {
|
|
1036
|
+
name: "antigravity",
|
|
1037
|
+
displayName: "Antigravity",
|
|
1038
|
+
skillsDir: ".agent/skills",
|
|
1039
|
+
globalSkillsDir: join(home, ".gemini/antigravity/skills"),
|
|
1040
|
+
detectInstalled: async () => {
|
|
1041
|
+
return existsSync(join(home, ".gemini/antigravity"));
|
|
1043
1042
|
}
|
|
1044
1043
|
},
|
|
1045
1044
|
"kiro-cli": {
|
|
@@ -1060,13 +1059,23 @@ const agents = {
|
|
|
1060
1059
|
return existsSync(join(home, ".codeium/windsurf"));
|
|
1061
1060
|
}
|
|
1062
1061
|
},
|
|
1063
|
-
|
|
1064
|
-
name: "
|
|
1065
|
-
displayName: "
|
|
1066
|
-
skillsDir: ".
|
|
1067
|
-
globalSkillsDir: join(home, ".
|
|
1062
|
+
cline: {
|
|
1063
|
+
name: "cline",
|
|
1064
|
+
displayName: "Cline",
|
|
1065
|
+
skillsDir: ".cline/skills",
|
|
1066
|
+
globalSkillsDir: join(home, ".cline", "skills"),
|
|
1067
|
+
supportsSharedAgentsDir: true,
|
|
1068
1068
|
detectInstalled: async () => {
|
|
1069
|
-
return existsSync(join(home, ".
|
|
1069
|
+
return existsSync(join(home, ".cline"));
|
|
1070
|
+
}
|
|
1071
|
+
},
|
|
1072
|
+
continue: {
|
|
1073
|
+
name: "continue",
|
|
1074
|
+
displayName: "Continue",
|
|
1075
|
+
skillsDir: ".continue/skills",
|
|
1076
|
+
globalSkillsDir: join(home, ".continue/skills"),
|
|
1077
|
+
detectInstalled: async () => {
|
|
1078
|
+
return existsSync(join(process.cwd(), ".continue")) || existsSync(join(home, ".continue"));
|
|
1070
1079
|
}
|
|
1071
1080
|
},
|
|
1072
1081
|
trae: {
|
|
@@ -1099,8 +1108,9 @@ const agents = {
|
|
|
1099
1108
|
warp: {
|
|
1100
1109
|
name: "warp",
|
|
1101
1110
|
displayName: "Warp",
|
|
1102
|
-
skillsDir: ".
|
|
1103
|
-
globalSkillsDir: join(home, ".
|
|
1111
|
+
skillsDir: ".warp/skills",
|
|
1112
|
+
globalSkillsDir: join(home, ".warp/skills"),
|
|
1113
|
+
supportsSharedAgentsDir: true,
|
|
1104
1114
|
detectInstalled: async () => {
|
|
1105
1115
|
return existsSync(join(home, ".warp"));
|
|
1106
1116
|
}
|
|
@@ -1128,6 +1138,7 @@ const agents = {
|
|
|
1128
1138
|
displayName: "Amp",
|
|
1129
1139
|
skillsDir: ".agents/skills",
|
|
1130
1140
|
globalSkillsDir: join(configHome, "agents/skills"),
|
|
1141
|
+
supportsSharedAgentsDir: true,
|
|
1131
1142
|
detectInstalled: async () => {
|
|
1132
1143
|
return existsSync(join(configHome, "amp"));
|
|
1133
1144
|
}
|
|
@@ -1272,6 +1283,7 @@ const agents = {
|
|
|
1272
1283
|
displayName: "Replit",
|
|
1273
1284
|
skillsDir: ".agents/skills",
|
|
1274
1285
|
globalSkillsDir: join(configHome, "agents/skills"),
|
|
1286
|
+
supportsSharedAgentsDir: true,
|
|
1275
1287
|
showInUniversalList: false,
|
|
1276
1288
|
detectInstalled: async () => {
|
|
1277
1289
|
return existsSync(join(process.cwd(), ".replit"));
|
|
@@ -1300,6 +1312,7 @@ const agents = {
|
|
|
1300
1312
|
displayName: "Universal",
|
|
1301
1313
|
skillsDir: ".agents/skills",
|
|
1302
1314
|
globalSkillsDir: join(configHome, "agents/skills"),
|
|
1315
|
+
supportsSharedAgentsDir: true,
|
|
1303
1316
|
showInUniversalList: false,
|
|
1304
1317
|
detectInstalled: async () => false
|
|
1305
1318
|
}
|
|
@@ -1311,18 +1324,18 @@ async function detectInstalledAgents() {
|
|
|
1311
1324
|
})))).filter((r) => r.installed).map((r) => r.type);
|
|
1312
1325
|
}
|
|
1313
1326
|
function getUniversalAgents() {
|
|
1314
|
-
return Object.entries(agents).filter(([
|
|
1327
|
+
return Object.entries(agents).filter(([, config]) => config.supportsSharedAgentsDir === true && config.showInUniversalList !== false).map(([type]) => type);
|
|
1315
1328
|
}
|
|
1316
1329
|
function getNonUniversalAgents() {
|
|
1317
|
-
return Object.entries(agents).filter(([
|
|
1330
|
+
return Object.entries(agents).filter(([, config]) => config.supportsSharedAgentsDir !== true).map(([type]) => type);
|
|
1318
1331
|
}
|
|
1319
1332
|
function isUniversalAgent(type) {
|
|
1320
|
-
return agents[type].
|
|
1333
|
+
return agents[type].supportsSharedAgentsDir === true;
|
|
1321
1334
|
}
|
|
1322
1335
|
const AGENTS_DIR$2 = ".agents";
|
|
1323
1336
|
const SKILLS_SUBDIR = "skills";
|
|
1324
1337
|
function sanitizeName(name) {
|
|
1325
|
-
return name.toLowerCase().replace(/[^a-z0-9._]+/g, "-").replace(/^[
|
|
1338
|
+
return name.toLowerCase().replace(/[^a-z0-9._]+/g, "-").replace(/^[.-]+|[.-]+$/g, "").substring(0, 255) || "unnamed-skill";
|
|
1326
1339
|
}
|
|
1327
1340
|
function isPathSafe(basePath, targetPath) {
|
|
1328
1341
|
const normalizedBase = normalize(resolve(basePath));
|
|
@@ -1332,6 +1345,27 @@ function isPathSafe(basePath, targetPath) {
|
|
|
1332
1345
|
function getCanonicalSkillsDir(global, cwd) {
|
|
1333
1346
|
return join(global ? homedir() : cwd || process.cwd(), AGENTS_DIR$2, SKILLS_SUBDIR);
|
|
1334
1347
|
}
|
|
1348
|
+
function isSharedDir(path) {
|
|
1349
|
+
return path === ".agents/skills" || path === "agents/skills";
|
|
1350
|
+
}
|
|
1351
|
+
function hasPrivateSkillsDir(agentType, global) {
|
|
1352
|
+
const agent = agents[agentType];
|
|
1353
|
+
if (global) {
|
|
1354
|
+
if (!agent.globalSkillsDir) return false;
|
|
1355
|
+
return !agent.globalSkillsDir.endsWith("/.agents/skills") && !agent.globalSkillsDir.endsWith("/agents/skills");
|
|
1356
|
+
}
|
|
1357
|
+
return !isSharedDir(agent.skillsDir);
|
|
1358
|
+
}
|
|
1359
|
+
function getIsolatedStoreBase(global, cwd) {
|
|
1360
|
+
return join(global ? homedir() : cwd || process.cwd(), ".skills-store", SKILLS_SUBDIR);
|
|
1361
|
+
}
|
|
1362
|
+
function getIsolatedStorePath(skillName, options = {}) {
|
|
1363
|
+
const sanitized = sanitizeName(skillName);
|
|
1364
|
+
const storeBase = getIsolatedStoreBase(options.global ?? false, options.cwd);
|
|
1365
|
+
const storePath = join(storeBase, sanitized);
|
|
1366
|
+
if (!isPathSafe(storeBase, storePath)) throw new Error("Invalid skill name: potential path traversal detected");
|
|
1367
|
+
return storePath;
|
|
1368
|
+
}
|
|
1335
1369
|
function getAgentBaseDir(agentType, global, cwd) {
|
|
1336
1370
|
if (isUniversalAgent(agentType)) return getCanonicalSkillsDir(global, cwd);
|
|
1337
1371
|
const agent = agents[agentType];
|
|
@@ -1342,6 +1376,17 @@ function getAgentBaseDir(agentType, global, cwd) {
|
|
|
1342
1376
|
}
|
|
1343
1377
|
return join(baseDir, agent.skillsDir);
|
|
1344
1378
|
}
|
|
1379
|
+
function getAgentPrivateBaseDir(agentType, global, cwd) {
|
|
1380
|
+
const agent = agents[agentType];
|
|
1381
|
+
const baseDir = global ? homedir() : cwd || process.cwd();
|
|
1382
|
+
if (global) {
|
|
1383
|
+
if (!agent.globalSkillsDir) return getCanonicalSkillsDir(global, cwd);
|
|
1384
|
+
if (hasPrivateSkillsDir(agentType, true)) return agent.globalSkillsDir;
|
|
1385
|
+
return getCanonicalSkillsDir(global, cwd);
|
|
1386
|
+
}
|
|
1387
|
+
if (hasPrivateSkillsDir(agentType, false)) return join(baseDir, agent.skillsDir);
|
|
1388
|
+
return getCanonicalSkillsDir(global, cwd);
|
|
1389
|
+
}
|
|
1345
1390
|
function resolveSymlinkTarget(linkPath, linkTarget) {
|
|
1346
1391
|
return resolve(dirname(linkPath), linkTarget);
|
|
1347
1392
|
}
|
|
@@ -1402,9 +1447,11 @@ async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
1402
1447
|
const skillName = sanitizeName(skill.name || basename(skill.path));
|
|
1403
1448
|
const canonicalBase = getCanonicalSkillsDir(isGlobal, cwd);
|
|
1404
1449
|
const canonicalDir = join(canonicalBase, skillName);
|
|
1405
|
-
const
|
|
1406
|
-
const
|
|
1450
|
+
const isolatedStoreBase = getIsolatedStoreBase(isGlobal, cwd);
|
|
1451
|
+
const isolatedStoreDir = join(isolatedStoreBase, skillName);
|
|
1407
1452
|
const installMode = options.mode ?? "symlink";
|
|
1453
|
+
const agentBase = installMode === "symlink-isolated" || installMode === "copy-isolated" ? getAgentPrivateBaseDir(agentType, isGlobal, cwd) : getAgentBaseDir(agentType, isGlobal, cwd);
|
|
1454
|
+
const agentDir = join(agentBase, skillName);
|
|
1408
1455
|
if (!isPathSafe(canonicalBase, canonicalDir)) return {
|
|
1409
1456
|
success: false,
|
|
1410
1457
|
path: agentDir,
|
|
@@ -1417,14 +1464,58 @@ async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
1417
1464
|
mode: installMode,
|
|
1418
1465
|
error: "Invalid skill name: potential path traversal detected"
|
|
1419
1466
|
};
|
|
1467
|
+
if (!isPathSafe(isolatedStoreBase, isolatedStoreDir)) return {
|
|
1468
|
+
success: false,
|
|
1469
|
+
path: agentDir,
|
|
1470
|
+
mode: installMode,
|
|
1471
|
+
error: "Invalid skill name: potential path traversal detected"
|
|
1472
|
+
};
|
|
1420
1473
|
try {
|
|
1421
1474
|
if (installMode === "copy") {
|
|
1475
|
+
const targetDir = join(isUniversalAgent(agentType) ? getCanonicalSkillsDir(isGlobal, cwd) : getAgentBaseDir(agentType, isGlobal, cwd), skillName);
|
|
1476
|
+
const sharedFallbackUsed = isUniversalAgent(agentType) && !hasPrivateSkillsDir(agentType, isGlobal);
|
|
1477
|
+
await cleanAndCreateDirectory(targetDir);
|
|
1478
|
+
await copyDirectory(skill.path, targetDir);
|
|
1479
|
+
return {
|
|
1480
|
+
success: true,
|
|
1481
|
+
path: targetDir,
|
|
1482
|
+
mode: "copy",
|
|
1483
|
+
sharedFallbackUsed
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
if (installMode === "copy-isolated") {
|
|
1487
|
+
const sharedFallbackUsed = !hasPrivateSkillsDir(agentType, isGlobal);
|
|
1422
1488
|
await cleanAndCreateDirectory(agentDir);
|
|
1423
1489
|
await copyDirectory(skill.path, agentDir);
|
|
1424
1490
|
return {
|
|
1425
1491
|
success: true,
|
|
1426
1492
|
path: agentDir,
|
|
1427
|
-
mode: "copy"
|
|
1493
|
+
mode: "copy-isolated",
|
|
1494
|
+
sharedFallbackUsed
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
if (installMode === "symlink-isolated") {
|
|
1498
|
+
const sharedFallbackUsed = !hasPrivateSkillsDir(agentType, isGlobal);
|
|
1499
|
+
await cleanAndCreateDirectory(isolatedStoreDir);
|
|
1500
|
+
await copyDirectory(skill.path, isolatedStoreDir);
|
|
1501
|
+
if (!await createSymlink(isolatedStoreDir, agentDir)) {
|
|
1502
|
+
await cleanAndCreateDirectory(agentDir);
|
|
1503
|
+
await copyDirectory(skill.path, agentDir);
|
|
1504
|
+
return {
|
|
1505
|
+
success: true,
|
|
1506
|
+
path: agentDir,
|
|
1507
|
+
canonicalPath: isolatedStoreDir,
|
|
1508
|
+
mode: "symlink-isolated",
|
|
1509
|
+
symlinkFailed: true,
|
|
1510
|
+
sharedFallbackUsed
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
return {
|
|
1514
|
+
success: true,
|
|
1515
|
+
path: agentDir,
|
|
1516
|
+
canonicalPath: isolatedStoreDir,
|
|
1517
|
+
mode: "symlink-isolated",
|
|
1518
|
+
sharedFallbackUsed
|
|
1428
1519
|
};
|
|
1429
1520
|
}
|
|
1430
1521
|
await cleanAndCreateDirectory(canonicalDir);
|
|
@@ -1506,8 +1597,6 @@ async function isSkillInstalled(skillName, agentType, options = {}) {
|
|
|
1506
1597
|
}
|
|
1507
1598
|
}
|
|
1508
1599
|
function getInstallPath(skillName, agentType, options = {}) {
|
|
1509
|
-
agents[agentType];
|
|
1510
|
-
options.cwd || process.cwd();
|
|
1511
1600
|
const sanitized = sanitizeName(skillName);
|
|
1512
1601
|
const targetBase = getAgentBaseDir(agentType, options.global ?? false, options.cwd);
|
|
1513
1602
|
const installPath = join(targetBase, sanitized);
|
|
@@ -1535,7 +1624,9 @@ async function installWellKnownSkillForAgent(skill, agentType, options = {}) {
|
|
|
1535
1624
|
const skillName = sanitizeName(skill.installName);
|
|
1536
1625
|
const canonicalBase = getCanonicalSkillsDir(isGlobal, cwd);
|
|
1537
1626
|
const canonicalDir = join(canonicalBase, skillName);
|
|
1538
|
-
const
|
|
1627
|
+
const isolatedStoreBase = getIsolatedStoreBase(isGlobal, cwd);
|
|
1628
|
+
const isolatedStoreDir = join(isolatedStoreBase, skillName);
|
|
1629
|
+
const agentBase = installMode === "symlink-isolated" || installMode === "copy-isolated" ? getAgentPrivateBaseDir(agentType, isGlobal, cwd) : getAgentBaseDir(agentType, isGlobal, cwd);
|
|
1539
1630
|
const agentDir = join(agentBase, skillName);
|
|
1540
1631
|
if (!isPathSafe(canonicalBase, canonicalDir)) return {
|
|
1541
1632
|
success: false,
|
|
@@ -1549,6 +1640,12 @@ async function installWellKnownSkillForAgent(skill, agentType, options = {}) {
|
|
|
1549
1640
|
mode: installMode,
|
|
1550
1641
|
error: "Invalid skill name: potential path traversal detected"
|
|
1551
1642
|
};
|
|
1643
|
+
if (!isPathSafe(isolatedStoreBase, isolatedStoreDir)) return {
|
|
1644
|
+
success: false,
|
|
1645
|
+
path: agentDir,
|
|
1646
|
+
mode: installMode,
|
|
1647
|
+
error: "Invalid skill name: potential path traversal detected"
|
|
1648
|
+
};
|
|
1552
1649
|
async function writeSkillFiles(targetDir) {
|
|
1553
1650
|
for (const [filePath, content] of skill.files) {
|
|
1554
1651
|
const fullPath = join(targetDir, filePath);
|
|
@@ -1560,12 +1657,50 @@ async function installWellKnownSkillForAgent(skill, agentType, options = {}) {
|
|
|
1560
1657
|
}
|
|
1561
1658
|
try {
|
|
1562
1659
|
if (installMode === "copy") {
|
|
1660
|
+
const targetDir = join(isUniversalAgent(agentType) ? getCanonicalSkillsDir(isGlobal, cwd) : getAgentBaseDir(agentType, isGlobal, cwd), skillName);
|
|
1661
|
+
const sharedFallbackUsed = isUniversalAgent(agentType) && !hasPrivateSkillsDir(agentType, isGlobal);
|
|
1662
|
+
await cleanAndCreateDirectory(targetDir);
|
|
1663
|
+
await writeSkillFiles(targetDir);
|
|
1664
|
+
return {
|
|
1665
|
+
success: true,
|
|
1666
|
+
path: targetDir,
|
|
1667
|
+
mode: "copy",
|
|
1668
|
+
sharedFallbackUsed
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
if (installMode === "copy-isolated") {
|
|
1672
|
+
const sharedFallbackUsed = !hasPrivateSkillsDir(agentType, isGlobal);
|
|
1563
1673
|
await cleanAndCreateDirectory(agentDir);
|
|
1564
1674
|
await writeSkillFiles(agentDir);
|
|
1565
1675
|
return {
|
|
1566
1676
|
success: true,
|
|
1567
1677
|
path: agentDir,
|
|
1568
|
-
mode: "copy"
|
|
1678
|
+
mode: "copy-isolated",
|
|
1679
|
+
sharedFallbackUsed
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
if (installMode === "symlink-isolated") {
|
|
1683
|
+
const sharedFallbackUsed = !hasPrivateSkillsDir(agentType, isGlobal);
|
|
1684
|
+
await cleanAndCreateDirectory(isolatedStoreDir);
|
|
1685
|
+
await writeSkillFiles(isolatedStoreDir);
|
|
1686
|
+
if (!await createSymlink(isolatedStoreDir, agentDir)) {
|
|
1687
|
+
await cleanAndCreateDirectory(agentDir);
|
|
1688
|
+
await writeSkillFiles(agentDir);
|
|
1689
|
+
return {
|
|
1690
|
+
success: true,
|
|
1691
|
+
path: agentDir,
|
|
1692
|
+
canonicalPath: isolatedStoreDir,
|
|
1693
|
+
mode: "symlink-isolated",
|
|
1694
|
+
symlinkFailed: true,
|
|
1695
|
+
sharedFallbackUsed
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
return {
|
|
1699
|
+
success: true,
|
|
1700
|
+
path: agentDir,
|
|
1701
|
+
canonicalPath: isolatedStoreDir,
|
|
1702
|
+
mode: "symlink-isolated",
|
|
1703
|
+
sharedFallbackUsed
|
|
1569
1704
|
};
|
|
1570
1705
|
}
|
|
1571
1706
|
await cleanAndCreateDirectory(canonicalDir);
|
|
@@ -1631,7 +1766,7 @@ async function listInstalledSkills(options = {}) {
|
|
|
1631
1766
|
for (const scope of scopes) try {
|
|
1632
1767
|
const entries = await readdir(scope.path, { withFileTypes: true });
|
|
1633
1768
|
for (const entry of entries) {
|
|
1634
|
-
if (!entry.isDirectory()) continue;
|
|
1769
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
1635
1770
|
const skillDir = join(scope.path, entry.name);
|
|
1636
1771
|
const skillMdPath = join(skillDir, "SKILL.md");
|
|
1637
1772
|
try {
|
|
@@ -1667,7 +1802,7 @@ async function listInstalledSkills(options = {}) {
|
|
|
1667
1802
|
const possibleNames = Array.from(new Set([
|
|
1668
1803
|
entry.name,
|
|
1669
1804
|
sanitizedSkillName,
|
|
1670
|
-
skill.name.toLowerCase().replace(/\s+/g, "-").replace(/[
|
|
1805
|
+
skill.name.toLowerCase().replace(/\s+/g, "-").replace(/[/\\:\0]/g, "")
|
|
1671
1806
|
]));
|
|
1672
1807
|
for (const possibleName of possibleNames) {
|
|
1673
1808
|
const agentSkillDir = join(agentBase, possibleName);
|
|
@@ -2205,7 +2340,7 @@ function logSkillParseFailures(failures) {
|
|
|
2205
2340
|
if (f.hint) M.message(import_picocolors.default.yellow(` 提示: ${f.hint}`));
|
|
2206
2341
|
}
|
|
2207
2342
|
}
|
|
2208
|
-
var version$1 = "2.0.
|
|
2343
|
+
var version$1 = "2.0.25";
|
|
2209
2344
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
2210
2345
|
function redactSensitiveText(text) {
|
|
2211
2346
|
return text.replace(/(https?:\/\/)([^/\s:@]+)(?::([^@/\s]*))?@/gi, "$1").replace(/([?&](?:private_token|access_token|token|api_key|access_key|secret|password)=)[^&\s]+/gi, "$1***");
|
|
@@ -2272,42 +2407,54 @@ function formatList$1(items, maxShow = 5) {
|
|
|
2272
2407
|
const remaining = items.length - maxShow;
|
|
2273
2408
|
return `${shown.join(", ")} +${remaining} more`;
|
|
2274
2409
|
}
|
|
2275
|
-
function
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
universal,
|
|
2282
|
-
symlinked
|
|
2283
|
-
};
|
|
2284
|
-
}
|
|
2285
|
-
function buildAgentSummaryLines(targetAgents, installMode) {
|
|
2286
|
-
const lines = [];
|
|
2287
|
-
const { universal, symlinked } = splitAgentsByType(targetAgents);
|
|
2288
|
-
if (installMode === "symlink") {
|
|
2289
|
-
if (universal.length > 0) lines.push(` ${import_picocolors.default.green("universal:")} ${formatList$1(universal)}`);
|
|
2290
|
-
if (symlinked.length > 0) lines.push(` ${import_picocolors.default.dim("symlink →")} ${formatList$1(symlinked)}`);
|
|
2291
|
-
} else {
|
|
2292
|
-
const allNames = targetAgents.map((a) => agents[a].displayName);
|
|
2293
|
-
lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$1(allNames)}`);
|
|
2294
|
-
}
|
|
2295
|
-
return lines;
|
|
2410
|
+
function getEntityPath(skillName, installMode, global, cwd) {
|
|
2411
|
+
if (installMode === "symlink-isolated" || installMode === "copy-isolated") return join(global ? homedir() : cwd, ".skills-store", "skills", skillName);
|
|
2412
|
+
return getCanonicalPath(skillName, {
|
|
2413
|
+
global,
|
|
2414
|
+
cwd
|
|
2415
|
+
});
|
|
2296
2416
|
}
|
|
2297
|
-
function
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2417
|
+
function getInstallMethodOptions() {
|
|
2418
|
+
return [
|
|
2419
|
+
{
|
|
2420
|
+
value: "symlink",
|
|
2421
|
+
label: "Symlink",
|
|
2422
|
+
hint: "拷贝技能到公认目录 .agents/skills【实现 agent 间共享】+ 软链到不支持公认目录的 agent 的私有目录"
|
|
2423
|
+
},
|
|
2424
|
+
{
|
|
2425
|
+
value: "symlink-isolated",
|
|
2426
|
+
label: "Symlink & Isolated",
|
|
2427
|
+
hint: "拷贝技能到隔离目录 .skills-store + 软链到各 agent 的私有目录【实现 agent 间的技能独享】"
|
|
2428
|
+
},
|
|
2429
|
+
{
|
|
2430
|
+
value: "copy",
|
|
2431
|
+
label: "Copy",
|
|
2432
|
+
hint: "拷贝技能到公认目录 .agents/skills【实现 agent 间共享】,不支持公认目录的 agent 会拷贝到其私有目录"
|
|
2433
|
+
},
|
|
2434
|
+
{
|
|
2435
|
+
value: "copy-isolated",
|
|
2436
|
+
label: "Copy & Isolated",
|
|
2437
|
+
hint: "拷贝技能到各 agent 的私有目录,不写入共享目录【实现 agent 间的技能独享】"
|
|
2438
|
+
}
|
|
2439
|
+
];
|
|
2302
2440
|
}
|
|
2303
|
-
function
|
|
2441
|
+
function buildAgentSummaryLines(targetAgents, installMode) {
|
|
2442
|
+
const allNames = targetAgents.map((a) => agents[a].displayName);
|
|
2443
|
+
return [` ${import_picocolors.default.dim(`${{
|
|
2444
|
+
symlink: "symlink(shared)",
|
|
2445
|
+
copy: "copy(shared)",
|
|
2446
|
+
"symlink-isolated": "symlink(isolated)",
|
|
2447
|
+
"copy-isolated": "copy(isolated)"
|
|
2448
|
+
}[installMode]} →`)} ${formatList$1(allNames)}`];
|
|
2449
|
+
}
|
|
2450
|
+
function buildResultLines(results) {
|
|
2304
2451
|
const lines = [];
|
|
2305
|
-
const
|
|
2306
|
-
const
|
|
2307
|
-
const
|
|
2308
|
-
if (
|
|
2309
|
-
if (
|
|
2310
|
-
if (
|
|
2452
|
+
const symlinked = results.filter((r) => !r.symlinkFailed).map((r) => r.agent);
|
|
2453
|
+
const copied = results.filter((r) => r.symlinkFailed).map((r) => r.agent);
|
|
2454
|
+
const sharedFallback = results.filter((r) => r.sharedFallbackUsed).map((r) => r.agent);
|
|
2455
|
+
if (symlinked.length > 0) lines.push(` ${import_picocolors.default.dim("symlinked:")} ${formatList$1(symlinked)}`);
|
|
2456
|
+
if (copied.length > 0) lines.push(` ${import_picocolors.default.yellow("copied:")} ${formatList$1(copied)}`);
|
|
2457
|
+
if (sharedFallback.length > 0) lines.push(` ${import_picocolors.default.yellow("shared fallback:")} ${formatList$1(sharedFallback)}`);
|
|
2311
2458
|
return lines;
|
|
2312
2459
|
}
|
|
2313
2460
|
function multiselect(opts) {
|
|
@@ -2323,55 +2470,65 @@ async function promptForAgents(message, choices) {
|
|
|
2323
2470
|
lastSelected = await getLastSelectedAgents();
|
|
2324
2471
|
} catch {}
|
|
2325
2472
|
const validAgents = choices.map((c) => c.value);
|
|
2326
|
-
const defaultValues = [
|
|
2327
|
-
"claude-code",
|
|
2328
|
-
"opencode",
|
|
2329
|
-
"codex"
|
|
2330
|
-
].filter((a) => validAgents.includes(a));
|
|
2331
2473
|
let initialValues = [];
|
|
2332
2474
|
if (lastSelected && lastSelected.length > 0) initialValues = lastSelected.filter((a) => validAgents.includes(a));
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2475
|
+
let needsReselect = true;
|
|
2476
|
+
while (needsReselect) {
|
|
2477
|
+
const selected = await searchMultiselect({
|
|
2478
|
+
message,
|
|
2479
|
+
items: choices,
|
|
2480
|
+
initialSelected: initialValues,
|
|
2481
|
+
required: true
|
|
2482
|
+
});
|
|
2483
|
+
if (isCancelled$1(selected)) return selected;
|
|
2484
|
+
if (selected.length === 0) {
|
|
2485
|
+
M.warn("请至少选择一个 agent");
|
|
2486
|
+
continue;
|
|
2487
|
+
}
|
|
2488
|
+
try {
|
|
2489
|
+
await saveSelectedAgents(selected);
|
|
2490
|
+
} catch {}
|
|
2491
|
+
needsReselect = false;
|
|
2492
|
+
return selected;
|
|
2493
|
+
}
|
|
2494
|
+
return [];
|
|
2344
2495
|
}
|
|
2345
2496
|
async function selectAgentsInteractive(options) {
|
|
2346
2497
|
const supportsGlobalFilter = (a) => !options.global || agents[a].globalSkillsDir;
|
|
2347
|
-
const
|
|
2348
|
-
const
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2498
|
+
const availableAgents = Object.keys(agents).filter(supportsGlobalFilter);
|
|
2499
|
+
const choices = availableAgents.map((a) => {
|
|
2500
|
+
const targetDir = options.global ? agents[a].globalSkillsDir : agents[a].skillsDir;
|
|
2501
|
+
const sharedSupport = agents[a].supportsSharedAgentsDir ? ", supports .agents/skills" : "";
|
|
2502
|
+
return {
|
|
2352
2503
|
value: a,
|
|
2353
|
-
label: agents[a].displayName
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
value: a,
|
|
2358
|
-
label: agents[a].displayName,
|
|
2359
|
-
hint: options.global ? agents[a].globalSkillsDir : agents[a].skillsDir
|
|
2360
|
-
}));
|
|
2504
|
+
label: agents[a].displayName,
|
|
2505
|
+
hint: `${targetDir}${sharedSupport}`
|
|
2506
|
+
};
|
|
2507
|
+
});
|
|
2361
2508
|
let lastSelected;
|
|
2362
2509
|
try {
|
|
2363
2510
|
lastSelected = await getLastSelectedAgents();
|
|
2364
2511
|
} catch {}
|
|
2365
|
-
const
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2512
|
+
const initialSelected = lastSelected ? lastSelected.filter((a) => availableAgents.includes(a)) : [];
|
|
2513
|
+
let needsReselect = true;
|
|
2514
|
+
while (needsReselect) {
|
|
2515
|
+
const selected = await searchMultiselect({
|
|
2516
|
+
message: "Which agents do you want to install to?",
|
|
2517
|
+
items: choices,
|
|
2518
|
+
initialSelected
|
|
2519
|
+
});
|
|
2520
|
+
if (isCancelled$1(selected)) return selected;
|
|
2521
|
+
if (selected.length === 0) {
|
|
2522
|
+
M.warn("请至少选择一个 agent");
|
|
2523
|
+
continue;
|
|
2524
|
+
}
|
|
2525
|
+
try {
|
|
2526
|
+
await saveSelectedAgents(selected);
|
|
2527
|
+
} catch {}
|
|
2528
|
+
needsReselect = false;
|
|
2529
|
+
return selected;
|
|
2530
|
+
}
|
|
2531
|
+
return [];
|
|
2375
2532
|
}
|
|
2376
2533
|
setVersion(version$1);
|
|
2377
2534
|
async function handleWellKnownSkills(source, url, options, spinner) {
|
|
@@ -2447,7 +2604,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2447
2604
|
M.info(`Valid agents: ${validAgents.join(", ")}`);
|
|
2448
2605
|
process.exit(1);
|
|
2449
2606
|
}
|
|
2450
|
-
targetAgents =
|
|
2607
|
+
targetAgents = options.agent;
|
|
2451
2608
|
} else {
|
|
2452
2609
|
spinner.start("Loading agents...");
|
|
2453
2610
|
const installedAgents = await detectInstalledAgents();
|
|
@@ -2469,7 +2626,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2469
2626
|
targetAgents = selected;
|
|
2470
2627
|
}
|
|
2471
2628
|
else if (installedAgents.length === 1 || options.yes) {
|
|
2472
|
-
targetAgents =
|
|
2629
|
+
targetAgents = installedAgents;
|
|
2473
2630
|
if (installedAgents.length === 1) {
|
|
2474
2631
|
const firstAgent = installedAgents[0];
|
|
2475
2632
|
M.info(`Installing to: ${import_picocolors.default.cyan(agents[firstAgent].displayName)}`);
|
|
@@ -2508,15 +2665,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2508
2665
|
if (!options.copy && !options.yes) {
|
|
2509
2666
|
const modeChoice = await ve({
|
|
2510
2667
|
message: "Installation method",
|
|
2511
|
-
options:
|
|
2512
|
-
value: "symlink",
|
|
2513
|
-
label: "Symlink (Recommended)",
|
|
2514
|
-
hint: "Single source of truth, easy updates"
|
|
2515
|
-
}, {
|
|
2516
|
-
value: "copy",
|
|
2517
|
-
label: "Copy to all agents",
|
|
2518
|
-
hint: "Independent copies for each agent"
|
|
2519
|
-
}]
|
|
2668
|
+
options: getInstallMethodOptions()
|
|
2520
2669
|
});
|
|
2521
2670
|
if (pD(modeChoice)) {
|
|
2522
2671
|
xe("Installation cancelled");
|
|
@@ -2538,7 +2687,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2538
2687
|
}
|
|
2539
2688
|
for (const skill of selectedSkills) {
|
|
2540
2689
|
if (summaryLines.length > 0) summaryLines.push("");
|
|
2541
|
-
const shortCanonical = shortenPath$2(
|
|
2690
|
+
const shortCanonical = shortenPath$2(getEntityPath(skill.installName, installMode, installGlobally, cwd), cwd);
|
|
2542
2691
|
summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
|
|
2543
2692
|
summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
|
|
2544
2693
|
if (skill.files.size > 1) summaryLines.push(` ${import_picocolors.default.dim("files:")} ${skill.files.size}`);
|
|
@@ -2557,7 +2706,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2557
2706
|
}
|
|
2558
2707
|
spinner.start("Installing skills...");
|
|
2559
2708
|
const results = [];
|
|
2560
|
-
const effectiveAgents = installMode === "copy" ? targetAgents.filter((a, i, arr) => arr.findIndex((b) => agents[b].skillsDir === agents[a].skillsDir) === i) : targetAgents;
|
|
2709
|
+
const effectiveAgents = installMode === "copy" || installMode === "copy-isolated" ? targetAgents.filter((a, i, arr) => arr.findIndex((b) => agents[b].skillsDir === agents[a].skillsDir) === i) : targetAgents;
|
|
2561
2710
|
for (const skill of selectedSkills) for (const agent of effectiveAgents) {
|
|
2562
2711
|
const result = await installWellKnownSkillForAgent(skill, agent, {
|
|
2563
2712
|
global: installGlobally,
|
|
@@ -2627,12 +2776,13 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2627
2776
|
bySkill.set(r.skill, skillResults);
|
|
2628
2777
|
}
|
|
2629
2778
|
const skillCount = bySkill.size;
|
|
2630
|
-
const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
|
|
2779
|
+
const symlinkFailures = successful.filter((r) => (r.mode === "symlink" || r.mode === "symlink-isolated") && r.symlinkFailed);
|
|
2631
2780
|
const copiedAgents = symlinkFailures.map((r) => r.agent);
|
|
2781
|
+
const sharedFallbackAgents = successful.filter((r) => r.sharedFallbackUsed).map((r) => r.agent);
|
|
2632
2782
|
const resultLines = [];
|
|
2633
2783
|
for (const [skillName, skillResults] of bySkill) {
|
|
2634
2784
|
const firstResult = skillResults[0];
|
|
2635
|
-
if (firstResult.mode === "copy") {
|
|
2785
|
+
if (firstResult.mode === "copy" || firstResult.mode === "copy-isolated") {
|
|
2636
2786
|
resultLines.push(`${import_picocolors.default.green("✓")} ${skillName} ${import_picocolors.default.dim("(copied)")}`);
|
|
2637
2787
|
for (const r of skillResults) {
|
|
2638
2788
|
const shortPath = shortenPath$2(r.path, cwd);
|
|
@@ -2643,7 +2793,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2643
2793
|
const shortPath = shortenPath$2(firstResult.canonicalPath, cwd);
|
|
2644
2794
|
resultLines.push(`${import_picocolors.default.green("✓")} ${shortPath}`);
|
|
2645
2795
|
} else resultLines.push(`${import_picocolors.default.green("✓")} ${skillName}`);
|
|
2646
|
-
resultLines.push(...buildResultLines(skillResults
|
|
2796
|
+
resultLines.push(...buildResultLines(skillResults));
|
|
2647
2797
|
}
|
|
2648
2798
|
}
|
|
2649
2799
|
const title = import_picocolors.default.green(`Installed ${skillCount} skill${skillCount !== 1 ? "s" : ""}`);
|
|
@@ -2652,6 +2802,10 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2652
2802
|
M.warn(import_picocolors.default.yellow(`Symlinks failed for: ${formatList$1(copiedAgents)}`));
|
|
2653
2803
|
M.message(import_picocolors.default.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
|
|
2654
2804
|
}
|
|
2805
|
+
if (sharedFallbackAgents.length > 0) {
|
|
2806
|
+
M.warn(import_picocolors.default.bgYellow(import_picocolors.default.black(" FALLBACK ")) + " " + import_picocolors.default.yellow(`No private skills dir for: ${formatList$1(sharedFallbackAgents)}`));
|
|
2807
|
+
M.message(import_picocolors.default.dim(" Installed to shared .agents/skills for compatibility"));
|
|
2808
|
+
}
|
|
2655
2809
|
}
|
|
2656
2810
|
if (failed.length > 0) {
|
|
2657
2811
|
console.log();
|
|
@@ -2947,7 +3101,7 @@ async function runAdd(args, options = {}) {
|
|
|
2947
3101
|
await cleanup(tempDir);
|
|
2948
3102
|
process.exit(1);
|
|
2949
3103
|
}
|
|
2950
|
-
targetAgents =
|
|
3104
|
+
targetAgents = options.agent;
|
|
2951
3105
|
} else {
|
|
2952
3106
|
spinner.start("Loading agents...");
|
|
2953
3107
|
const installedAgents = await detectInstalledAgents();
|
|
@@ -2970,7 +3124,7 @@ async function runAdd(args, options = {}) {
|
|
|
2970
3124
|
targetAgents = selected;
|
|
2971
3125
|
}
|
|
2972
3126
|
else if (installedAgents.length === 1 || options.yes) {
|
|
2973
|
-
targetAgents =
|
|
3127
|
+
targetAgents = installedAgents;
|
|
2974
3128
|
if (installedAgents.length === 1) {
|
|
2975
3129
|
const firstAgent = installedAgents[0];
|
|
2976
3130
|
M.info(`Installing to: ${import_picocolors.default.cyan(agents[firstAgent].displayName)}`);
|
|
@@ -3011,15 +3165,7 @@ async function runAdd(args, options = {}) {
|
|
|
3011
3165
|
if (!options.copy && !options.yes) {
|
|
3012
3166
|
const modeChoice = await ve({
|
|
3013
3167
|
message: "Installation method",
|
|
3014
|
-
options:
|
|
3015
|
-
value: "symlink",
|
|
3016
|
-
label: "Symlink (Recommended)",
|
|
3017
|
-
hint: "Single source of truth, easy updates"
|
|
3018
|
-
}, {
|
|
3019
|
-
value: "copy",
|
|
3020
|
-
label: "Copy to all agents",
|
|
3021
|
-
hint: "Independent copies for each agent"
|
|
3022
|
-
}]
|
|
3168
|
+
options: getInstallMethodOptions()
|
|
3023
3169
|
});
|
|
3024
3170
|
if (pD(modeChoice)) {
|
|
3025
3171
|
xe("Installation cancelled");
|
|
@@ -3050,7 +3196,7 @@ async function runAdd(args, options = {}) {
|
|
|
3050
3196
|
const printSkillSummary = (skills) => {
|
|
3051
3197
|
for (const skill of skills) {
|
|
3052
3198
|
if (summaryLines.length > 0) summaryLines.push("");
|
|
3053
|
-
const shortCanonical = shortenPath$2(
|
|
3199
|
+
const shortCanonical = shortenPath$2(getEntityPath(skill.name, installMode, installGlobally, cwd), cwd);
|
|
3054
3200
|
summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
|
|
3055
3201
|
summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
|
|
3056
3202
|
const skillOverwrites = overwriteStatus.get(skill.name);
|
|
@@ -3094,7 +3240,7 @@ async function runAdd(args, options = {}) {
|
|
|
3094
3240
|
}
|
|
3095
3241
|
spinner.start("Installing skills...");
|
|
3096
3242
|
const results = [];
|
|
3097
|
-
const effectiveAgents = installMode === "copy" ? targetAgents.filter((a, i, arr) => arr.findIndex((b) => agents[b].skillsDir === agents[a].skillsDir) === i) : targetAgents;
|
|
3243
|
+
const effectiveAgents = installMode === "copy" || installMode === "copy-isolated" ? targetAgents.filter((a, i, arr) => arr.findIndex((b) => agents[b].skillsDir === agents[a].skillsDir) === i) : targetAgents;
|
|
3098
3244
|
for (const skill of selectedSkills) for (const agent of effectiveAgents) {
|
|
3099
3245
|
const result = await installSkillForAgent(skill, agent, {
|
|
3100
3246
|
global: installGlobally,
|
|
@@ -3264,14 +3410,15 @@ async function runAdd(args, options = {}) {
|
|
|
3264
3410
|
} else ungroupedResults.push(r);
|
|
3265
3411
|
}
|
|
3266
3412
|
const skillCount = bySkill.size;
|
|
3267
|
-
const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
|
|
3413
|
+
const symlinkFailures = successful.filter((r) => (r.mode === "symlink" || r.mode === "symlink-isolated") && r.symlinkFailed);
|
|
3268
3414
|
const copiedAgents = symlinkFailures.map((r) => r.agent);
|
|
3415
|
+
const sharedFallbackAgents = successful.filter((r) => r.sharedFallbackUsed).map((r) => r.agent);
|
|
3269
3416
|
const resultLines = [];
|
|
3270
3417
|
const printSkillResults = (entries) => {
|
|
3271
3418
|
for (const entry of entries) {
|
|
3272
3419
|
const skillResults = bySkill.get(entry.skill) || [];
|
|
3273
3420
|
const firstResult = skillResults[0];
|
|
3274
|
-
if (firstResult.mode === "copy") {
|
|
3421
|
+
if (firstResult.mode === "copy" || firstResult.mode === "copy-isolated") {
|
|
3275
3422
|
resultLines.push(`${import_picocolors.default.green("✓")} ${entry.skill} ${import_picocolors.default.dim("(copied)")}`);
|
|
3276
3423
|
for (const r of skillResults) {
|
|
3277
3424
|
const shortPath = shortenPath$2(r.path, cwd);
|
|
@@ -3282,7 +3429,7 @@ async function runAdd(args, options = {}) {
|
|
|
3282
3429
|
const shortPath = shortenPath$2(firstResult.canonicalPath, cwd);
|
|
3283
3430
|
resultLines.push(`${import_picocolors.default.green("✓")} ${shortPath}`);
|
|
3284
3431
|
} else resultLines.push(`${import_picocolors.default.green("✓")} ${entry.skill}`);
|
|
3285
|
-
resultLines.push(...buildResultLines(skillResults
|
|
3432
|
+
resultLines.push(...buildResultLines(skillResults));
|
|
3286
3433
|
}
|
|
3287
3434
|
}
|
|
3288
3435
|
};
|
|
@@ -3306,6 +3453,10 @@ async function runAdd(args, options = {}) {
|
|
|
3306
3453
|
M.warn(import_picocolors.default.yellow(`Symlinks failed for: ${formatList$1(copiedAgents)}`));
|
|
3307
3454
|
M.message(import_picocolors.default.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
|
|
3308
3455
|
}
|
|
3456
|
+
if (sharedFallbackAgents.length > 0) {
|
|
3457
|
+
M.warn(import_picocolors.default.bgYellow(import_picocolors.default.black(" FALLBACK ")) + " " + import_picocolors.default.yellow(`No private skills dir for: ${formatList$1(sharedFallbackAgents)}`));
|
|
3458
|
+
M.message(import_picocolors.default.dim(" Installed to shared .agents/skills for compatibility"));
|
|
3459
|
+
}
|
|
3309
3460
|
}
|
|
3310
3461
|
if (failed.length > 0) {
|
|
3311
3462
|
console.log();
|
|
@@ -4140,6 +4291,10 @@ async function removeCommand(skillNames, options) {
|
|
|
4140
4291
|
global: isGlobal,
|
|
4141
4292
|
cwd
|
|
4142
4293
|
});
|
|
4294
|
+
const isolatedStorePath = getIsolatedStorePath(skillName, {
|
|
4295
|
+
global: isGlobal,
|
|
4296
|
+
cwd
|
|
4297
|
+
});
|
|
4143
4298
|
for (const agentKey of targetAgents) {
|
|
4144
4299
|
const agent = agents[agentKey];
|
|
4145
4300
|
const skillPath = getInstallPath(skillName, agentKey, {
|
|
@@ -4171,10 +4326,16 @@ async function removeCommand(skillNames, options) {
|
|
|
4171
4326
|
isStillUsed = true;
|
|
4172
4327
|
break;
|
|
4173
4328
|
}
|
|
4174
|
-
if (!isStillUsed)
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4329
|
+
if (!isStillUsed) {
|
|
4330
|
+
await rm(canonicalPath, {
|
|
4331
|
+
recursive: true,
|
|
4332
|
+
force: true
|
|
4333
|
+
});
|
|
4334
|
+
await rm(isolatedStorePath, {
|
|
4335
|
+
recursive: true,
|
|
4336
|
+
force: true
|
|
4337
|
+
});
|
|
4338
|
+
}
|
|
4178
4339
|
const lockEntry = isGlobal ? await getSkillFromLock(skillName) : null;
|
|
4179
4340
|
const effectiveSource = lockEntry?.source || "local";
|
|
4180
4341
|
const effectiveSourceType = lockEntry?.sourceType || "local";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ali-skills",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25",
|
|
4
4
|
"description": "The open agent skills ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"author": "",
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ali/cli-skills": "^2.0.
|
|
34
|
+
"@ali/cli-skills": "^2.0.25"
|
|
35
35
|
},
|
|
36
36
|
"bundleDependencies": [
|
|
37
37
|
"@ali/cli-skills"
|