add-skill 1.0.22 → 1.0.23
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/dist/index.js +75 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -447,7 +447,13 @@ async function createSymlink(target, linkPath) {
|
|
|
447
447
|
} else {
|
|
448
448
|
await rm2(linkPath, { recursive: true });
|
|
449
449
|
}
|
|
450
|
-
} catch {
|
|
450
|
+
} catch (err) {
|
|
451
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ELOOP") {
|
|
452
|
+
try {
|
|
453
|
+
await rm2(linkPath, { force: true });
|
|
454
|
+
} catch {
|
|
455
|
+
}
|
|
456
|
+
}
|
|
451
457
|
}
|
|
452
458
|
const linkDir = join4(linkPath, "..");
|
|
453
459
|
await mkdir(linkDir, { recursive: true });
|
|
@@ -469,10 +475,12 @@ async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
469
475
|
const canonicalDir = join4(canonicalBase, skillName);
|
|
470
476
|
const agentBase = isGlobal ? agent.globalSkillsDir : join4(cwd, agent.skillsDir);
|
|
471
477
|
const agentDir = join4(agentBase, skillName);
|
|
478
|
+
const installMode = options.mode ?? "symlink";
|
|
472
479
|
if (!isPathSafe(canonicalBase, canonicalDir)) {
|
|
473
480
|
return {
|
|
474
481
|
success: false,
|
|
475
482
|
path: agentDir,
|
|
483
|
+
mode: installMode,
|
|
476
484
|
error: "Invalid skill name: potential path traversal detected"
|
|
477
485
|
};
|
|
478
486
|
}
|
|
@@ -480,32 +488,49 @@ async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
480
488
|
return {
|
|
481
489
|
success: false,
|
|
482
490
|
path: agentDir,
|
|
491
|
+
mode: installMode,
|
|
483
492
|
error: "Invalid skill name: potential path traversal detected"
|
|
484
493
|
};
|
|
485
494
|
}
|
|
486
495
|
try {
|
|
496
|
+
if (installMode === "copy") {
|
|
497
|
+
await mkdir(agentDir, { recursive: true });
|
|
498
|
+
await copyDirectory(skill.path, agentDir);
|
|
499
|
+
return {
|
|
500
|
+
success: true,
|
|
501
|
+
path: agentDir,
|
|
502
|
+
mode: "copy"
|
|
503
|
+
};
|
|
504
|
+
}
|
|
487
505
|
await mkdir(canonicalDir, { recursive: true });
|
|
488
506
|
await copyDirectory(skill.path, canonicalDir);
|
|
489
507
|
const symlinkCreated = await createSymlink(canonicalDir, agentDir);
|
|
490
508
|
if (!symlinkCreated) {
|
|
509
|
+
try {
|
|
510
|
+
await rm2(agentDir, { recursive: true, force: true });
|
|
511
|
+
} catch {
|
|
512
|
+
}
|
|
491
513
|
await mkdir(agentDir, { recursive: true });
|
|
492
514
|
await copyDirectory(skill.path, agentDir);
|
|
493
515
|
return {
|
|
494
516
|
success: true,
|
|
495
517
|
path: agentDir,
|
|
496
518
|
canonicalPath: canonicalDir,
|
|
519
|
+
mode: "symlink",
|
|
497
520
|
symlinkFailed: true
|
|
498
521
|
};
|
|
499
522
|
}
|
|
500
523
|
return {
|
|
501
524
|
success: true,
|
|
502
525
|
path: agentDir,
|
|
503
|
-
canonicalPath: canonicalDir
|
|
526
|
+
canonicalPath: canonicalDir,
|
|
527
|
+
mode: "symlink"
|
|
504
528
|
};
|
|
505
529
|
} catch (error) {
|
|
506
530
|
return {
|
|
507
531
|
success: false,
|
|
508
532
|
path: agentDir,
|
|
533
|
+
mode: installMode,
|
|
509
534
|
error: error instanceof Error ? error.message : "Unknown error"
|
|
510
535
|
};
|
|
511
536
|
}
|
|
@@ -599,7 +624,7 @@ function track(data) {
|
|
|
599
624
|
// package.json
|
|
600
625
|
var package_default = {
|
|
601
626
|
name: "add-skill",
|
|
602
|
-
version: "1.0.
|
|
627
|
+
version: "1.0.23",
|
|
603
628
|
description: "Install agent skills onto coding agents (OpenCode, Claude Code, Codex, Cursor)",
|
|
604
629
|
type: "module",
|
|
605
630
|
bin: {
|
|
@@ -886,6 +911,22 @@ async function main(source, options) {
|
|
|
886
911
|
}
|
|
887
912
|
installGlobally = scope;
|
|
888
913
|
}
|
|
914
|
+
let installMode = "symlink";
|
|
915
|
+
if (!options.yes) {
|
|
916
|
+
const modeChoice = await p.select({
|
|
917
|
+
message: "Installation method",
|
|
918
|
+
options: [
|
|
919
|
+
{ value: "symlink", label: "Symlink (Recommended)", hint: "Single source of truth, easy updates" },
|
|
920
|
+
{ value: "copy", label: "Copy to all agents", hint: "Independent copies for each agent" }
|
|
921
|
+
]
|
|
922
|
+
});
|
|
923
|
+
if (p.isCancel(modeChoice)) {
|
|
924
|
+
p.cancel("Installation cancelled");
|
|
925
|
+
await cleanup(tempDir);
|
|
926
|
+
process.exit(0);
|
|
927
|
+
}
|
|
928
|
+
installMode = modeChoice;
|
|
929
|
+
}
|
|
889
930
|
const cwd = process.cwd();
|
|
890
931
|
const summaryLines = [];
|
|
891
932
|
const overwriteStatus = /* @__PURE__ */ new Map();
|
|
@@ -902,12 +943,17 @@ async function main(source, options) {
|
|
|
902
943
|
);
|
|
903
944
|
for (const skill of selectedSkills) {
|
|
904
945
|
if (summaryLines.length > 0) summaryLines.push("");
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
946
|
+
if (installMode === "symlink") {
|
|
947
|
+
const canonicalPath = getCanonicalPath(skill.name, { global: installGlobally });
|
|
948
|
+
const shortCanonical = shortenPath(canonicalPath, cwd);
|
|
949
|
+
summaryLines.push(`${chalk.cyan(shortCanonical)}`);
|
|
950
|
+
summaryLines.push(` ${chalk.dim("symlink \u2192")} ${formatList(agentNames)}`);
|
|
951
|
+
} else {
|
|
952
|
+
summaryLines.push(`${chalk.cyan(getSkillDisplayName(skill))}`);
|
|
953
|
+
summaryLines.push(` ${chalk.dim("copy \u2192")} ${formatList(agentNames)}`);
|
|
954
|
+
}
|
|
908
955
|
const skillOverwrites = overwriteStatus.get(skill.name);
|
|
909
956
|
const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
|
|
910
|
-
summaryLines.push(` ${chalk.dim("\u2192")} ${formatList(agentNames)}`);
|
|
911
957
|
if (overwriteAgents.length > 0) {
|
|
912
958
|
summaryLines.push(` ${chalk.yellow("overwrites:")} ${formatList(overwriteAgents)}`);
|
|
913
959
|
}
|
|
@@ -926,7 +972,7 @@ async function main(source, options) {
|
|
|
926
972
|
const results = [];
|
|
927
973
|
for (const skill of selectedSkills) {
|
|
928
974
|
for (const agent of targetAgents) {
|
|
929
|
-
const result = await installSkillForAgent(skill, agent, { global: installGlobally });
|
|
975
|
+
const result = await installSkillForAgent(skill, agent, { global: installGlobally, mode: installMode });
|
|
930
976
|
results.push({
|
|
931
977
|
skill: getSkillDisplayName(skill),
|
|
932
978
|
agent: agents[agent].displayName,
|
|
@@ -970,22 +1016,30 @@ async function main(source, options) {
|
|
|
970
1016
|
}
|
|
971
1017
|
const skillCount = bySkill.size;
|
|
972
1018
|
const agentCount = new Set(successful.map((r) => r.agent)).size;
|
|
973
|
-
const symlinkFailures = successful.filter((r) => r.symlinkFailed);
|
|
1019
|
+
const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
|
|
974
1020
|
const copiedAgents = symlinkFailures.map((r) => r.agent);
|
|
975
1021
|
const resultLines = [];
|
|
976
|
-
for (const [, skillResults] of bySkill) {
|
|
1022
|
+
for (const [skillName, skillResults] of bySkill) {
|
|
977
1023
|
const firstResult = skillResults[0];
|
|
978
|
-
if (firstResult.
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1024
|
+
if (firstResult.mode === "copy") {
|
|
1025
|
+
resultLines.push(`${chalk.green("\u2713")} ${skillName} ${chalk.dim("(copied)")}`);
|
|
1026
|
+
for (const r of skillResults) {
|
|
1027
|
+
const shortPath = shortenPath(r.path, cwd);
|
|
1028
|
+
resultLines.push(` ${chalk.dim("\u2192")} ${shortPath}`);
|
|
1029
|
+
}
|
|
1030
|
+
} else {
|
|
1031
|
+
if (firstResult.canonicalPath) {
|
|
1032
|
+
const shortPath = shortenPath(firstResult.canonicalPath, cwd);
|
|
1033
|
+
resultLines.push(`${chalk.green("\u2713")} ${shortPath}`);
|
|
1034
|
+
}
|
|
1035
|
+
const symlinked = skillResults.filter((r) => !r.symlinkFailed).map((r) => r.agent);
|
|
1036
|
+
const copied = skillResults.filter((r) => r.symlinkFailed).map((r) => r.agent);
|
|
1037
|
+
if (symlinked.length > 0) {
|
|
1038
|
+
resultLines.push(` ${chalk.dim("symlink \u2192")} ${formatList(symlinked)}`);
|
|
1039
|
+
}
|
|
1040
|
+
if (copied.length > 0) {
|
|
1041
|
+
resultLines.push(` ${chalk.yellow("copied \u2192")} ${formatList(copied)}`);
|
|
1042
|
+
}
|
|
989
1043
|
}
|
|
990
1044
|
}
|
|
991
1045
|
const title = chalk.green(`Installed ${skillCount} skill${skillCount !== 1 ? "s" : ""} to ${agentCount} agent${agentCount !== 1 ? "s" : ""}`);
|