skills 1.5.12 → 1.5.13
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 +3 -0
- package/dist/cli.mjs +243 -51
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/cli.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import { t as require_dist } from "./_chunks/libs/@vercel/detect-agent.mjs";
|
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "fs";
|
|
11
11
|
import { basename, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "path";
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
13
14
|
import { stripVTControlCharacters } from "node:util";
|
|
14
15
|
import { homedir, platform, tmpdir } from "os";
|
|
15
16
|
import * as readline from "readline";
|
|
@@ -18,8 +19,7 @@ import { promisify } from "util";
|
|
|
18
19
|
import { execFile, execSync, spawn, spawnSync } from "child_process";
|
|
19
20
|
import { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rm, stat, symlink, writeFile } from "fs/promises";
|
|
20
21
|
import { parse } from "yaml";
|
|
21
|
-
import { createHash } from "crypto";
|
|
22
|
-
import { createHash as createHash$1 } from "node:crypto";
|
|
22
|
+
import { createHash as createHash$1 } from "crypto";
|
|
23
23
|
import { gunzipSync, inflateRawSync } from "node:zlib";
|
|
24
24
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
25
25
|
function getOwnerRepo(parsed) {
|
|
@@ -720,7 +720,7 @@ async function computeSkillFolderHash(skillDir) {
|
|
|
720
720
|
const files = [];
|
|
721
721
|
await collectFiles(skillDir, skillDir, files);
|
|
722
722
|
files.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
723
|
-
const hash = createHash("sha256");
|
|
723
|
+
const hash = createHash$1("sha256");
|
|
724
724
|
for (const file of files) {
|
|
725
725
|
hash.update(file.relativePath);
|
|
726
726
|
hash.update(file.content);
|
|
@@ -1617,6 +1617,16 @@ async function detectInstalledAgents() {
|
|
|
1617
1617
|
installed: await config.detectInstalled()
|
|
1618
1618
|
})))).filter((r) => r.installed).map((r) => r.type);
|
|
1619
1619
|
}
|
|
1620
|
+
const EVE_SUBAGENTS_DIR = join("agent", "subagents");
|
|
1621
|
+
function getEveSubagents(cwd = process.cwd()) {
|
|
1622
|
+
const dir = join(cwd, EVE_SUBAGENTS_DIR);
|
|
1623
|
+
if (!existsSync(dir)) return [];
|
|
1624
|
+
try {
|
|
1625
|
+
return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
1626
|
+
} catch {
|
|
1627
|
+
return [];
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1620
1630
|
function getUniversalAgents() {
|
|
1621
1631
|
return Object.entries(agents).filter(([_, config]) => config.skillsDir === ".agents/skills" && config.showInUniversalList !== false).map(([type]) => type);
|
|
1622
1632
|
}
|
|
@@ -1654,8 +1664,12 @@ async function isDirEntryOrSymlinkToDir(entry, entryPath) {
|
|
|
1654
1664
|
function getCanonicalSkillsDir(global, cwd) {
|
|
1655
1665
|
return join(global ? homedir() : cwd || process.cwd(), AGENTS_DIR$1, SKILLS_SUBDIR);
|
|
1656
1666
|
}
|
|
1657
|
-
function
|
|
1667
|
+
function getEveSubagentSkillsDir(subagent, cwd) {
|
|
1668
|
+
return join(cwd || process.cwd(), EVE_SUBAGENTS_DIR, sanitizeName(subagent), "skills");
|
|
1669
|
+
}
|
|
1670
|
+
function getAgentBaseDir(agentType, global, cwd, eveSubagent) {
|
|
1658
1671
|
if (isUniversalAgent(agentType)) return getCanonicalSkillsDir(global, cwd);
|
|
1672
|
+
if (agentType === "eve" && eveSubagent) return getEveSubagentSkillsDir(eveSubagent, cwd);
|
|
1659
1673
|
const agent = agents[agentType];
|
|
1660
1674
|
const baseDir = global ? homedir() : cwd || process.cwd();
|
|
1661
1675
|
if (global) {
|
|
@@ -1717,6 +1731,7 @@ async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
1717
1731
|
const agent = agents[agentType];
|
|
1718
1732
|
const isGlobal = options.global ?? false;
|
|
1719
1733
|
const cwd = options.cwd || process.cwd();
|
|
1734
|
+
const eveSubagent = options.eveSubagent;
|
|
1720
1735
|
if (isGlobal && agent.globalSkillsDir === void 0) return {
|
|
1721
1736
|
success: false,
|
|
1722
1737
|
path: "",
|
|
@@ -1725,9 +1740,9 @@ async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
1725
1740
|
};
|
|
1726
1741
|
const skillName = sanitizeName(skill.name || basename(skill.path));
|
|
1727
1742
|
const installMode = options.mode ?? "symlink";
|
|
1728
|
-
const canonicalBase = agentType === "eve" && installMode === "symlink" ? getAgentBaseDir(agentType, isGlobal, cwd) : getCanonicalSkillsDir(isGlobal, cwd);
|
|
1743
|
+
const canonicalBase = agentType === "eve" && installMode === "symlink" ? getAgentBaseDir(agentType, isGlobal, cwd, eveSubagent) : getCanonicalSkillsDir(isGlobal, cwd);
|
|
1729
1744
|
const canonicalDir = join(canonicalBase, skillName);
|
|
1730
|
-
const agentBase = getAgentBaseDir(agentType, isGlobal, cwd);
|
|
1745
|
+
const agentBase = getAgentBaseDir(agentType, isGlobal, cwd, eveSubagent);
|
|
1731
1746
|
const agentDir = join(agentBase, skillName);
|
|
1732
1747
|
if (!isPathSafe$1(canonicalBase, canonicalDir)) return {
|
|
1733
1748
|
success: false,
|
|
@@ -1869,7 +1884,7 @@ async function isSkillInstalled(skillName, agentType, options = {}) {
|
|
|
1869
1884
|
const agent = agents[agentType];
|
|
1870
1885
|
const sanitized = sanitizeName(skillName);
|
|
1871
1886
|
if (options.global && agent.globalSkillsDir === void 0) return false;
|
|
1872
|
-
const targetBase = options.global ? agent.globalSkillsDir : join(options.cwd || process.cwd(), agent.skillsDir);
|
|
1887
|
+
const targetBase = options.global ? agent.globalSkillsDir : agentType === "eve" && options.eveSubagent ? getEveSubagentSkillsDir(options.eveSubagent, options.cwd) : join(options.cwd || process.cwd(), agent.skillsDir);
|
|
1873
1888
|
const skillDir = join(targetBase, sanitized);
|
|
1874
1889
|
if (!isPathSafe$1(targetBase, skillDir)) return false;
|
|
1875
1890
|
try {
|
|
@@ -1883,14 +1898,14 @@ function getInstallPath(skillName, agentType, options = {}) {
|
|
|
1883
1898
|
agents[agentType];
|
|
1884
1899
|
options.cwd || process.cwd();
|
|
1885
1900
|
const sanitized = sanitizeName(skillName);
|
|
1886
|
-
const targetBase = getAgentBaseDir(agentType, options.global ?? false, options.cwd);
|
|
1901
|
+
const targetBase = getAgentBaseDir(agentType, options.global ?? false, options.cwd, options.eveSubagent);
|
|
1887
1902
|
const installPath = join(targetBase, sanitized);
|
|
1888
1903
|
if (!isPathSafe$1(targetBase, installPath)) throw new Error("Invalid skill name: potential path traversal detected");
|
|
1889
1904
|
return installPath;
|
|
1890
1905
|
}
|
|
1891
1906
|
function getCanonicalPath(skillName, options = {}) {
|
|
1892
1907
|
const sanitized = sanitizeName(skillName);
|
|
1893
|
-
const canonicalBase = options.agent === "eve" ? getAgentBaseDir("eve", options.global ?? false, options.cwd) : getCanonicalSkillsDir(options.global ?? false, options.cwd);
|
|
1908
|
+
const canonicalBase = options.agent === "eve" ? getAgentBaseDir("eve", options.global ?? false, options.cwd, options.eveSubagent) : getCanonicalSkillsDir(options.global ?? false, options.cwd);
|
|
1894
1909
|
const canonicalPath = join(canonicalBase, sanitized);
|
|
1895
1910
|
if (!isPathSafe$1(canonicalBase, canonicalPath)) throw new Error("Invalid skill name: potential path traversal detected");
|
|
1896
1911
|
return canonicalPath;
|
|
@@ -1900,6 +1915,7 @@ async function installWellKnownSkillForAgent(skill, agentType, options = {}) {
|
|
|
1900
1915
|
const isGlobal = options.global ?? false;
|
|
1901
1916
|
const cwd = options.cwd || process.cwd();
|
|
1902
1917
|
const installMode = options.mode ?? "symlink";
|
|
1918
|
+
const eveSubagent = options.eveSubagent;
|
|
1903
1919
|
if (isGlobal && agent.globalSkillsDir === void 0) return {
|
|
1904
1920
|
success: false,
|
|
1905
1921
|
path: "",
|
|
@@ -1907,9 +1923,9 @@ async function installWellKnownSkillForAgent(skill, agentType, options = {}) {
|
|
|
1907
1923
|
error: `${agent.displayName} does not support global skill installation`
|
|
1908
1924
|
};
|
|
1909
1925
|
const skillName = sanitizeName(skill.installName);
|
|
1910
|
-
const canonicalBase = agentType === "eve" && installMode === "symlink" ? getAgentBaseDir(agentType, isGlobal, cwd) : getCanonicalSkillsDir(isGlobal, cwd);
|
|
1926
|
+
const canonicalBase = agentType === "eve" && installMode === "symlink" ? getAgentBaseDir(agentType, isGlobal, cwd, eveSubagent) : getCanonicalSkillsDir(isGlobal, cwd);
|
|
1911
1927
|
const canonicalDir = join(canonicalBase, skillName);
|
|
1912
|
-
const agentBase = getAgentBaseDir(agentType, isGlobal, cwd);
|
|
1928
|
+
const agentBase = getAgentBaseDir(agentType, isGlobal, cwd, eveSubagent);
|
|
1913
1929
|
const agentDir = join(agentBase, skillName);
|
|
1914
1930
|
if (!isPathSafe$1(canonicalBase, canonicalDir)) return {
|
|
1915
1931
|
success: false,
|
|
@@ -1981,6 +1997,7 @@ async function installBlobSkillForAgent(skill, agentType, options = {}) {
|
|
|
1981
1997
|
const isGlobal = options.global ?? false;
|
|
1982
1998
|
const cwd = options.cwd || process.cwd();
|
|
1983
1999
|
const installMode = options.mode ?? "symlink";
|
|
2000
|
+
const eveSubagent = options.eveSubagent;
|
|
1984
2001
|
if (isGlobal && agent.globalSkillsDir === void 0) return {
|
|
1985
2002
|
success: false,
|
|
1986
2003
|
path: "",
|
|
@@ -1988,7 +2005,7 @@ async function installBlobSkillForAgent(skill, agentType, options = {}) {
|
|
|
1988
2005
|
error: `${agent.displayName} does not support global skill installation`
|
|
1989
2006
|
};
|
|
1990
2007
|
const skillName = sanitizeName(skill.installName);
|
|
1991
|
-
const agentBase = getAgentBaseDir(agentType, isGlobal, cwd);
|
|
2008
|
+
const agentBase = getAgentBaseDir(agentType, isGlobal, cwd, eveSubagent);
|
|
1992
2009
|
if (agentType === "eve" && !isEvePackagedSkill(skill.files)) {
|
|
1993
2010
|
const flatSkillPath = join(agentBase, toEveFlatSkillFileName(skill.installName));
|
|
1994
2011
|
if (!isPathSafe$1(agentBase, flatSkillPath)) return {
|
|
@@ -2018,7 +2035,7 @@ async function installBlobSkillForAgent(skill, agentType, options = {}) {
|
|
|
2018
2035
|
};
|
|
2019
2036
|
}
|
|
2020
2037
|
}
|
|
2021
|
-
const canonicalBase = agentType === "eve" && installMode === "symlink" ? getAgentBaseDir(agentType, isGlobal, cwd) : getCanonicalSkillsDir(isGlobal, cwd);
|
|
2038
|
+
const canonicalBase = agentType === "eve" && installMode === "symlink" ? getAgentBaseDir(agentType, isGlobal, cwd, eveSubagent) : getCanonicalSkillsDir(isGlobal, cwd);
|
|
2022
2039
|
const canonicalDir = join(canonicalBase, skillName);
|
|
2023
2040
|
const agentDir = join(agentBase, skillName);
|
|
2024
2041
|
if (!isPathSafe$1(canonicalBase, canonicalDir)) return {
|
|
@@ -2119,6 +2136,14 @@ async function listInstalledSkills(options = {}) {
|
|
|
2119
2136
|
path: agentDir,
|
|
2120
2137
|
agentType
|
|
2121
2138
|
});
|
|
2139
|
+
if (agentType === "eve" && !isGlobal) for (const subagent of getEveSubagents(cwd)) {
|
|
2140
|
+
const subagentDir = getEveSubagentSkillsDir(subagent, cwd);
|
|
2141
|
+
if (!scopes.some((s) => s.path === subagentDir && s.global === isGlobal)) scopes.push({
|
|
2142
|
+
global: isGlobal,
|
|
2143
|
+
path: subagentDir,
|
|
2144
|
+
agentType
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2122
2147
|
}
|
|
2123
2148
|
const allAgentTypes = Object.keys(agents);
|
|
2124
2149
|
for (const agentType of allAgentTypes) {
|
|
@@ -2623,7 +2648,7 @@ var WellKnownProvider = class {
|
|
|
2623
2648
|
}
|
|
2624
2649
|
}
|
|
2625
2650
|
computeDigest(bytes) {
|
|
2626
|
-
return `sha256:${createHash
|
|
2651
|
+
return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
|
|
2627
2652
|
}
|
|
2628
2653
|
extractArchive(bytes, artifactUrl, contentType) {
|
|
2629
2654
|
if (this.isZipArchive(bytes, artifactUrl, contentType)) return this.extractZip(bytes);
|
|
@@ -3041,6 +3066,14 @@ async function fetchSkillDownload(source, slug) {
|
|
|
3041
3066
|
return null;
|
|
3042
3067
|
}
|
|
3043
3068
|
}
|
|
3069
|
+
function computeSnapshotHash(files) {
|
|
3070
|
+
const hash = createHash("sha256");
|
|
3071
|
+
for (const file of [...files].sort((a, b) => a.path.localeCompare(b.path))) {
|
|
3072
|
+
hash.update(file.path);
|
|
3073
|
+
hash.update(file.contents);
|
|
3074
|
+
}
|
|
3075
|
+
return hash.digest("hex");
|
|
3076
|
+
}
|
|
3044
3077
|
async function tryBlobInstall(ownerRepo, options = {}) {
|
|
3045
3078
|
const tree = await fetchRepoTree(ownerRepo, options.ref, options.getToken);
|
|
3046
3079
|
if (!tree) return null;
|
|
@@ -3099,22 +3132,22 @@ async function tryBlobInstall(ownerRepo, options = {}) {
|
|
|
3099
3132
|
return {
|
|
3100
3133
|
skills: downloads.map(({ skill, download }) => {
|
|
3101
3134
|
const mdPathLower = skill.mdPath.toLowerCase();
|
|
3102
|
-
mdPathLower.endsWith("/skill.md") ? skill.mdPath.slice(0, -9) : mdPathLower === "skill.md"
|
|
3135
|
+
const files = (mdPathLower.endsWith("/skill.md") ? skill.mdPath.slice(0, -9) : mdPathLower === "skill.md" ? "" : skill.mdPath.slice(0, -9)) ? download.files : download.files.filter((file) => file.path.toLowerCase() === "skill.md");
|
|
3103
3136
|
return {
|
|
3104
3137
|
name: skill.name,
|
|
3105
3138
|
description: skill.description,
|
|
3106
3139
|
path: "",
|
|
3107
3140
|
rawContent: skill.content,
|
|
3108
3141
|
metadata: skill.metadata,
|
|
3109
|
-
files
|
|
3110
|
-
snapshotHash: download.hash,
|
|
3142
|
+
files,
|
|
3143
|
+
snapshotHash: files.length === download.files.length ? download.hash : computeSnapshotHash(files),
|
|
3111
3144
|
repoPath: skill.mdPath
|
|
3112
3145
|
};
|
|
3113
3146
|
}),
|
|
3114
3147
|
tree
|
|
3115
3148
|
};
|
|
3116
3149
|
}
|
|
3117
|
-
var version$1 = "1.5.
|
|
3150
|
+
var version$1 = "1.5.13";
|
|
3118
3151
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
3119
3152
|
async function isSourcePrivate(source) {
|
|
3120
3153
|
const ownerRepo = parseOwnerRepo(source);
|
|
@@ -3175,6 +3208,12 @@ function shortenPath$2(fullPath, cwd) {
|
|
|
3175
3208
|
if (fullPath === cwd || fullPath.startsWith(cwd + sep)) return "." + fullPath.slice(cwd.length);
|
|
3176
3209
|
return fullPath;
|
|
3177
3210
|
}
|
|
3211
|
+
function computeSingleFileSkillHash(contents) {
|
|
3212
|
+
const hash = createHash("sha256");
|
|
3213
|
+
hash.update("SKILL.md");
|
|
3214
|
+
hash.update(contents);
|
|
3215
|
+
return hash.digest("hex");
|
|
3216
|
+
}
|
|
3178
3217
|
function formatList$1(items, maxShow = 5) {
|
|
3179
3218
|
if (items.length <= maxShow) return items.join(", ");
|
|
3180
3219
|
const shown = items.slice(0, maxShow);
|
|
@@ -3203,6 +3242,37 @@ function buildAgentSummaryLines(targetAgents, installMode) {
|
|
|
3203
3242
|
}
|
|
3204
3243
|
return lines;
|
|
3205
3244
|
}
|
|
3245
|
+
function targetDisplayName(target) {
|
|
3246
|
+
const base = agents[target.agent].displayName;
|
|
3247
|
+
return target.subagent ? `${base} (${target.subagent})` : base;
|
|
3248
|
+
}
|
|
3249
|
+
function targetKey(target) {
|
|
3250
|
+
return target.subagent ? `${target.agent}:${target.subagent}` : target.agent;
|
|
3251
|
+
}
|
|
3252
|
+
function buildInstallTargets(targetAgents, eveSubagentTargets) {
|
|
3253
|
+
const targets = [];
|
|
3254
|
+
for (const agent of targetAgents) if (agent === "eve") for (const subagent of eveSubagentTargets) targets.push({
|
|
3255
|
+
agent,
|
|
3256
|
+
subagent
|
|
3257
|
+
});
|
|
3258
|
+
else targets.push({ agent });
|
|
3259
|
+
return targets;
|
|
3260
|
+
}
|
|
3261
|
+
function buildTargetSummaryLines(targets, installMode) {
|
|
3262
|
+
const lines = [];
|
|
3263
|
+
const rootAgents = targets.filter((t) => !t.subagent).map((t) => t.agent);
|
|
3264
|
+
const subagentNames = targets.filter((t) => t.subagent).map(targetDisplayName);
|
|
3265
|
+
const { universal, symlinked } = splitAgentsByType(rootAgents);
|
|
3266
|
+
if (installMode === "symlink") {
|
|
3267
|
+
if (universal.length > 0) lines.push(` ${import_picocolors.default.green("universal:")} ${formatList$1(universal)}`);
|
|
3268
|
+
if (symlinked.length > 0) lines.push(` ${import_picocolors.default.dim("symlink →")} ${formatList$1(symlinked)}`);
|
|
3269
|
+
if (subagentNames.length > 0) lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$1(subagentNames)}`);
|
|
3270
|
+
} else {
|
|
3271
|
+
const allNames = targets.map(targetDisplayName);
|
|
3272
|
+
lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$1(allNames)}`);
|
|
3273
|
+
}
|
|
3274
|
+
return lines;
|
|
3275
|
+
}
|
|
3206
3276
|
function ensureUniversalAgents(targetAgents) {
|
|
3207
3277
|
const universalAgents = getUniversalAgents();
|
|
3208
3278
|
const result = [...targetAgents];
|
|
@@ -3872,6 +3942,35 @@ async function runAdd(args, options = {}) {
|
|
|
3872
3942
|
targetAgents = selected;
|
|
3873
3943
|
}
|
|
3874
3944
|
}
|
|
3945
|
+
if (options.subagent && options.subagent.length > 0 && !targetAgents.includes("eve")) targetAgents = [...targetAgents, "eve"];
|
|
3946
|
+
let eveSubagentTargets = [void 0];
|
|
3947
|
+
if (targetAgents.includes("eve")) {
|
|
3948
|
+
const availableSubagents = getEveSubagents(process.cwd());
|
|
3949
|
+
if (options.subagent && options.subagent.length > 0) eveSubagentTargets = options.subagent.map((s) => s === "root" || s === "." ? void 0 : s);
|
|
3950
|
+
else if (availableSubagents.length > 0 && !options.yes) {
|
|
3951
|
+
const selectedSubagents = await fe({
|
|
3952
|
+
message: "Where should Eve skills be installed?",
|
|
3953
|
+
options: [{
|
|
3954
|
+
value: "",
|
|
3955
|
+
label: "Root agent",
|
|
3956
|
+
hint: "agent/skills"
|
|
3957
|
+
}, ...availableSubagents.map((name) => ({
|
|
3958
|
+
value: name,
|
|
3959
|
+
label: name,
|
|
3960
|
+
hint: `agent/subagents/${name}/skills`
|
|
3961
|
+
}))],
|
|
3962
|
+
initialValues: [""],
|
|
3963
|
+
required: true
|
|
3964
|
+
});
|
|
3965
|
+
if (pD(selectedSubagents)) {
|
|
3966
|
+
xe("Installation cancelled");
|
|
3967
|
+
await cleanup(tempDir);
|
|
3968
|
+
process.exit(0);
|
|
3969
|
+
}
|
|
3970
|
+
eveSubagentTargets = selectedSubagents.map((s) => s === "" ? void 0 : s);
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
const installTargets = buildInstallTargets(targetAgents, eveSubagentTargets);
|
|
3875
3974
|
let installGlobally = options.global ?? false;
|
|
3876
3975
|
const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
|
|
3877
3976
|
if (options.global === void 0 && !options.yes && supportsGlobal) {
|
|
@@ -3895,8 +3994,9 @@ async function runAdd(args, options = {}) {
|
|
|
3895
3994
|
installGlobally = scope;
|
|
3896
3995
|
}
|
|
3897
3996
|
let installMode = options.copy ? "copy" : "symlink";
|
|
3898
|
-
const
|
|
3899
|
-
|
|
3997
|
+
const allEve = installTargets.every((t) => t.agent === "eve");
|
|
3998
|
+
const uniqueDirs = new Set(installTargets.map((t) => t.subagent ? `eve:subagent:${t.subagent}` : agents[t.agent].skillsDir));
|
|
3999
|
+
if (!options.copy && !options.yes && uniqueDirs.size > 1 && !allEve) {
|
|
3900
4000
|
const modeChoice = await ve({
|
|
3901
4001
|
message: "Installation method",
|
|
3902
4002
|
options: [{
|
|
@@ -3915,19 +4015,21 @@ async function runAdd(args, options = {}) {
|
|
|
3915
4015
|
process.exit(0);
|
|
3916
4016
|
}
|
|
3917
4017
|
installMode = modeChoice;
|
|
3918
|
-
} else if (uniqueDirs.size <= 1) installMode = "copy";
|
|
4018
|
+
} else if (uniqueDirs.size <= 1 || allEve) installMode = "copy";
|
|
3919
4019
|
const cwd = process.cwd();
|
|
3920
4020
|
const summaryLines = [];
|
|
3921
|
-
|
|
3922
|
-
const overwriteChecks = await Promise.all(selectedSkills.flatMap((skill) => targetAgents.map(async (agent) => ({
|
|
4021
|
+
const overwriteChecks = await Promise.all(selectedSkills.flatMap((skill) => installTargets.map(async (target) => ({
|
|
3923
4022
|
skillName: skill.name,
|
|
3924
|
-
|
|
3925
|
-
installed: await isSkillInstalled(skill.name, agent, {
|
|
4023
|
+
target,
|
|
4024
|
+
installed: await isSkillInstalled(skill.name, target.agent, {
|
|
4025
|
+
global: installGlobally,
|
|
4026
|
+
eveSubagent: target.subagent
|
|
4027
|
+
})
|
|
3926
4028
|
}))));
|
|
3927
4029
|
const overwriteStatus = /* @__PURE__ */ new Map();
|
|
3928
|
-
for (const { skillName,
|
|
4030
|
+
for (const { skillName, target, installed } of overwriteChecks) {
|
|
3929
4031
|
if (!overwriteStatus.has(skillName)) overwriteStatus.set(skillName, /* @__PURE__ */ new Map());
|
|
3930
|
-
overwriteStatus.get(skillName).set(
|
|
4032
|
+
overwriteStatus.get(skillName).set(targetKey(target), installed);
|
|
3931
4033
|
}
|
|
3932
4034
|
const groupedSummary = {};
|
|
3933
4035
|
const ungroupedSummary = [];
|
|
@@ -3939,14 +4041,15 @@ async function runAdd(args, options = {}) {
|
|
|
3939
4041
|
const printSkillSummary = (skills) => {
|
|
3940
4042
|
for (const skill of skills) {
|
|
3941
4043
|
if (summaryLines.length > 0) summaryLines.push("");
|
|
3942
|
-
const shortCanonical = shortenPath$2(
|
|
4044
|
+
const shortCanonical = shortenPath$2(installTargets.length === 1 ? getCanonicalPath(skill.name, {
|
|
3943
4045
|
global: installGlobally,
|
|
3944
|
-
agent:
|
|
4046
|
+
agent: installTargets[0].agent,
|
|
4047
|
+
eveSubagent: installTargets[0].subagent
|
|
3945
4048
|
}) : getCanonicalPath(skill.name, { global: installGlobally }), cwd);
|
|
3946
4049
|
summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
|
|
3947
|
-
summaryLines.push(...
|
|
4050
|
+
summaryLines.push(...buildTargetSummaryLines(installTargets, installMode));
|
|
3948
4051
|
const skillOverwrites = overwriteStatus.get(skill.name);
|
|
3949
|
-
const overwriteAgents =
|
|
4052
|
+
const overwriteAgents = installTargets.filter((t) => skillOverwrites?.get(targetKey(t))).map(targetDisplayName);
|
|
3950
4053
|
if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
|
|
3951
4054
|
}
|
|
3952
4055
|
};
|
|
@@ -3986,7 +4089,8 @@ async function runAdd(args, options = {}) {
|
|
|
3986
4089
|
}
|
|
3987
4090
|
spinner.start("Installing skills...");
|
|
3988
4091
|
const results = [];
|
|
3989
|
-
for (const skill of selectedSkills) for (const
|
|
4092
|
+
for (const skill of selectedSkills) for (const target of installTargets) {
|
|
4093
|
+
const { agent, subagent } = target;
|
|
3990
4094
|
let result;
|
|
3991
4095
|
if (blobResult && "files" in skill) {
|
|
3992
4096
|
const blobSkill = skill;
|
|
@@ -3995,15 +4099,27 @@ async function runAdd(args, options = {}) {
|
|
|
3995
4099
|
files: blobSkill.files
|
|
3996
4100
|
}, agent, {
|
|
3997
4101
|
global: installGlobally,
|
|
3998
|
-
mode: installMode
|
|
4102
|
+
mode: installMode,
|
|
4103
|
+
eveSubagent: subagent
|
|
3999
4104
|
});
|
|
4000
|
-
} else result = await
|
|
4105
|
+
} else if (tempDir && skill.path === tempDir && skill.rawContent) result = await installBlobSkillForAgent({
|
|
4106
|
+
installName: skill.name,
|
|
4107
|
+
files: [{
|
|
4108
|
+
path: "SKILL.md",
|
|
4109
|
+
contents: skill.rawContent
|
|
4110
|
+
}]
|
|
4111
|
+
}, agent, {
|
|
4001
4112
|
global: installGlobally,
|
|
4002
4113
|
mode: installMode
|
|
4003
4114
|
});
|
|
4115
|
+
else result = await installSkillForAgent(skill, agent, {
|
|
4116
|
+
global: installGlobally,
|
|
4117
|
+
mode: installMode,
|
|
4118
|
+
eveSubagent: subagent
|
|
4119
|
+
});
|
|
4004
4120
|
results.push({
|
|
4005
4121
|
skill: getSkillDisplayName(skill),
|
|
4006
|
-
agent:
|
|
4122
|
+
agent: targetDisplayName(target),
|
|
4007
4123
|
pluginName: skill.pluginName,
|
|
4008
4124
|
...result
|
|
4009
4125
|
});
|
|
@@ -4069,17 +4185,20 @@ async function runAdd(args, options = {}) {
|
|
|
4069
4185
|
}
|
|
4070
4186
|
if (successful.length > 0 && !installGlobally) {
|
|
4071
4187
|
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
4188
|
+
const eveSubagents = targetAgents.includes("eve") ? eveSubagentTargets.map((s) => s ?? "") : void 0;
|
|
4189
|
+
const recordSubagents = eveSubagents && (eveSubagents.length > 1 || eveSubagents.some((s) => s !== ""));
|
|
4072
4190
|
for (const skill of selectedSkills) {
|
|
4073
4191
|
const skillDisplayName = getSkillDisplayName(skill);
|
|
4074
4192
|
if (successfulSkillNames.has(skillDisplayName)) try {
|
|
4075
|
-
const computedHash = blobResult && "snapshotHash" in skill ? skill.snapshotHash : await computeSkillFolderHash(skill.path);
|
|
4193
|
+
const computedHash = blobResult && "snapshotHash" in skill ? skill.snapshotHash : tempDir && skill.path === tempDir && skill.rawContent ? computeSingleFileSkillHash(skill.rawContent) : await computeSkillFolderHash(skill.path);
|
|
4076
4194
|
const skillPathValue = skillFiles[skill.name];
|
|
4077
4195
|
await addSkillToLocalLock(skill.name, {
|
|
4078
4196
|
source: lockSource || parsed.url,
|
|
4079
4197
|
ref: parsed.ref,
|
|
4080
4198
|
sourceType: parsed.type,
|
|
4081
4199
|
...skillPathValue && { skillPath: skillPathValue },
|
|
4082
|
-
computedHash
|
|
4200
|
+
computedHash,
|
|
4201
|
+
...recordSubagents && { subagents: eveSubagents }
|
|
4083
4202
|
}, cwd);
|
|
4084
4203
|
} catch {}
|
|
4085
4204
|
}
|
|
@@ -4237,7 +4356,17 @@ function parseAddOptions(args) {
|
|
|
4237
4356
|
i--;
|
|
4238
4357
|
} else if (arg === "--full-depth") options.fullDepth = true;
|
|
4239
4358
|
else if (arg === "--copy") options.copy = true;
|
|
4240
|
-
else if (arg === "--
|
|
4359
|
+
else if (arg === "--subagent") {
|
|
4360
|
+
options.subagent = options.subagent || [];
|
|
4361
|
+
i++;
|
|
4362
|
+
let nextArg = args[i];
|
|
4363
|
+
while (i < args.length && nextArg && !nextArg.startsWith("-")) {
|
|
4364
|
+
options.subagent.push(nextArg);
|
|
4365
|
+
i++;
|
|
4366
|
+
nextArg = args[i];
|
|
4367
|
+
}
|
|
4368
|
+
i--;
|
|
4369
|
+
} else if (arg === "--dangerously-accept-openclaw-risks") options.dangerouslyAcceptOpenclawRisks = true;
|
|
4241
4370
|
else if (arg && !arg.startsWith("-")) source.push(arg);
|
|
4242
4371
|
}
|
|
4243
4372
|
return {
|
|
@@ -4257,9 +4386,54 @@ function formatInstalls(count) {
|
|
|
4257
4386
|
if (count >= 1e3) return `${(count / 1e3).toFixed(1).replace(/\.0$/, "")}K installs`;
|
|
4258
4387
|
return `${count} install${count === 1 ? "" : "s"}`;
|
|
4259
4388
|
}
|
|
4260
|
-
|
|
4389
|
+
const GITHUB_OWNER_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,38})$/i;
|
|
4390
|
+
function parseFindOptions(args) {
|
|
4391
|
+
const queryParts = [];
|
|
4392
|
+
const options = {};
|
|
4393
|
+
const errors = [];
|
|
4394
|
+
for (let i = 0; i < args.length; i++) {
|
|
4395
|
+
const arg = args[i];
|
|
4396
|
+
if (!arg) continue;
|
|
4397
|
+
let ownerValue;
|
|
4398
|
+
if (arg === "--owner") {
|
|
4399
|
+
const value = args[i + 1];
|
|
4400
|
+
if (!value || value.startsWith("-")) {
|
|
4401
|
+
errors.push("--owner requires a GitHub owner");
|
|
4402
|
+
continue;
|
|
4403
|
+
}
|
|
4404
|
+
ownerValue = value;
|
|
4405
|
+
i++;
|
|
4406
|
+
} else if (arg.startsWith("--owner=")) {
|
|
4407
|
+
ownerValue = arg.slice(8);
|
|
4408
|
+
if (!ownerValue) {
|
|
4409
|
+
errors.push("--owner requires a GitHub owner");
|
|
4410
|
+
continue;
|
|
4411
|
+
}
|
|
4412
|
+
} else {
|
|
4413
|
+
queryParts.push(arg);
|
|
4414
|
+
continue;
|
|
4415
|
+
}
|
|
4416
|
+
const owner = ownerValue.trim().toLowerCase();
|
|
4417
|
+
if (!GITHUB_OWNER_PATTERN.test(owner)) {
|
|
4418
|
+
errors.push("--owner must be a valid GitHub owner");
|
|
4419
|
+
continue;
|
|
4420
|
+
}
|
|
4421
|
+
options.owner = owner;
|
|
4422
|
+
}
|
|
4423
|
+
return {
|
|
4424
|
+
query: queryParts.join(" "),
|
|
4425
|
+
options,
|
|
4426
|
+
errors
|
|
4427
|
+
};
|
|
4428
|
+
}
|
|
4429
|
+
async function searchSkillsAPI(query, owner) {
|
|
4261
4430
|
try {
|
|
4262
|
-
const
|
|
4431
|
+
const params = new URLSearchParams({
|
|
4432
|
+
q: query,
|
|
4433
|
+
limit: "10"
|
|
4434
|
+
});
|
|
4435
|
+
if (owner) params.set("owner", owner);
|
|
4436
|
+
const url = `${SEARCH_API_BASE}/api/search?${params.toString()}`;
|
|
4263
4437
|
const res = await fetch(url);
|
|
4264
4438
|
if (!res.ok) return [];
|
|
4265
4439
|
return (await res.json()).skills.map((skill) => ({
|
|
@@ -4277,7 +4451,7 @@ const SHOW_CURSOR = "\x1B[?25h";
|
|
|
4277
4451
|
const CLEAR_DOWN = "\x1B[J";
|
|
4278
4452
|
const MOVE_UP = (n) => `\x1b[${n}A`;
|
|
4279
4453
|
const MOVE_TO_COL = (n) => `\x1b[${n}G`;
|
|
4280
|
-
async function runSearchPrompt(initialQuery = "") {
|
|
4454
|
+
async function runSearchPrompt(initialQuery = "", owner) {
|
|
4281
4455
|
let results = [];
|
|
4282
4456
|
let selectedIndex = 0;
|
|
4283
4457
|
let query = initialQuery;
|
|
@@ -4334,7 +4508,7 @@ async function runSearchPrompt(initialQuery = "") {
|
|
|
4334
4508
|
const debounceMs = Math.max(150, 350 - q.length * 50);
|
|
4335
4509
|
debounceTimer = setTimeout(async () => {
|
|
4336
4510
|
try {
|
|
4337
|
-
results = await searchSkillsAPI(q);
|
|
4511
|
+
results = await searchSkillsAPI(q, owner);
|
|
4338
4512
|
selectedIndex = 0;
|
|
4339
4513
|
} catch {
|
|
4340
4514
|
results = [];
|
|
@@ -4407,20 +4581,27 @@ async function isRepoPublic(owner, repo) {
|
|
|
4407
4581
|
return await isRepoPrivate(owner, repo) === false;
|
|
4408
4582
|
}
|
|
4409
4583
|
async function runFind(args) {
|
|
4410
|
-
const query = args
|
|
4584
|
+
const { query, options: findOptions, errors } = parseFindOptions(args);
|
|
4585
|
+
const owner = findOptions.owner;
|
|
4411
4586
|
const isNonInteractive = !process.stdin.isTTY;
|
|
4412
4587
|
const agentTip = `${DIM$3}Tip: if running in a coding agent, follow these steps:${RESET$3}
|
|
4413
|
-
${DIM$3} 1) npx skills find [query]${RESET$3}
|
|
4588
|
+
${DIM$3} 1) npx skills find [query] [--owner <owner>]${RESET$3}
|
|
4414
4589
|
${DIM$3} 2) npx skills add <owner/repo@skill>${RESET$3}`;
|
|
4590
|
+
if (errors.length > 0) {
|
|
4591
|
+
for (const error of errors) console.error(error);
|
|
4592
|
+
console.error("Usage: npx skills find <query> [--owner <owner>]");
|
|
4593
|
+
return;
|
|
4594
|
+
}
|
|
4415
4595
|
if (query) {
|
|
4416
|
-
const results = await searchSkillsAPI(query);
|
|
4596
|
+
const results = await searchSkillsAPI(query, owner);
|
|
4417
4597
|
track({
|
|
4418
4598
|
event: "find",
|
|
4419
4599
|
query,
|
|
4420
4600
|
resultCount: String(results.length)
|
|
4421
4601
|
});
|
|
4422
4602
|
if (results.length === 0) {
|
|
4423
|
-
|
|
4603
|
+
const ownerSuffix = owner ? ` from owner "${owner}"` : "";
|
|
4604
|
+
console.log(`${DIM$3}No skills found for "${query}"${ownerSuffix}${RESET$3}`);
|
|
4424
4605
|
return;
|
|
4425
4606
|
}
|
|
4426
4607
|
console.log(`${DIM$3}Install with${RESET$3} npx skills add <owner/repo@skill>`);
|
|
@@ -4437,10 +4618,10 @@ ${DIM$3} 2) npx skills add <owner/repo@skill>${RESET$3}`;
|
|
|
4437
4618
|
if (isNonInteractive || await isRunningInAgent()) {
|
|
4438
4619
|
console.log(agentTip);
|
|
4439
4620
|
console.log();
|
|
4440
|
-
console.log(`${DIM$3}Usage: npx skills find <query
|
|
4621
|
+
console.log(`${DIM$3}Usage: npx skills find <query> [--owner <owner>]${RESET$3}`);
|
|
4441
4622
|
return;
|
|
4442
4623
|
}
|
|
4443
|
-
const selected = await runSearchPrompt();
|
|
4624
|
+
const selected = await runSearchPrompt("", owner);
|
|
4444
4625
|
track({
|
|
4445
4626
|
event: "find",
|
|
4446
4627
|
query: "",
|
|
@@ -4457,12 +4638,12 @@ ${DIM$3} 2) npx skills add <owner/repo@skill>${RESET$3}`;
|
|
|
4457
4638
|
console.log();
|
|
4458
4639
|
console.log(`${TEXT$2}Installing ${BOLD$3}${skillName}${RESET$3} from ${DIM$3}${pkg}${RESET$3}...`);
|
|
4459
4640
|
console.log();
|
|
4460
|
-
const { source, options } = parseAddOptions([
|
|
4641
|
+
const { source, options: addOptions } = parseAddOptions([
|
|
4461
4642
|
pkg,
|
|
4462
4643
|
"--skill",
|
|
4463
4644
|
skillName
|
|
4464
4645
|
]);
|
|
4465
|
-
await runAdd(source,
|
|
4646
|
+
await runAdd(source, addOptions);
|
|
4466
4647
|
console.log();
|
|
4467
4648
|
const info = getOwnerRepoFromString(pkg);
|
|
4468
4649
|
if (info && await isRepoPublic(info.owner, info.repo)) console.log(`${DIM$3}View the skill at${RESET$3} ${TEXT$2}https://skills.sh/${selected.slug}${RESET$3}`);
|
|
@@ -4982,6 +5163,7 @@ async function removeCommand(skillNames, options) {
|
|
|
4982
5163
|
} else {
|
|
4983
5164
|
await scanDir(getCanonicalSkillsDir(false, cwd));
|
|
4984
5165
|
for (const agent of Object.values(agents)) await scanDir(join(cwd, agent.skillsDir));
|
|
5166
|
+
for (const subagent of getEveSubagents(cwd)) await scanDir(getEveSubagentSkillsDir(subagent, cwd));
|
|
4985
5167
|
}
|
|
4986
5168
|
const installedSkills = Array.from(skillNamesSet).sort();
|
|
4987
5169
|
spinner.stop(`Found ${installedSkills.length} unique installed skill(s)`);
|
|
@@ -5055,7 +5237,10 @@ async function removeCommand(skillNames, options) {
|
|
|
5055
5237
|
const pathsToCleanup = new Set([skillPath]);
|
|
5056
5238
|
const sanitizedName = sanitizeName(skillName);
|
|
5057
5239
|
if (isGlobal && agent.globalSkillsDir) pathsToCleanup.add(join(agent.globalSkillsDir, sanitizedName));
|
|
5058
|
-
else
|
|
5240
|
+
else {
|
|
5241
|
+
pathsToCleanup.add(join(cwd, agent.skillsDir, sanitizedName));
|
|
5242
|
+
if (agentKey === "eve") for (const subagent of getEveSubagents(cwd)) pathsToCleanup.add(join(getEveSubagentSkillsDir(subagent, cwd), sanitizedName));
|
|
5243
|
+
}
|
|
5059
5244
|
for (const pathToCleanup of pathsToCleanup) {
|
|
5060
5245
|
if (pathToCleanup === canonicalPath) continue;
|
|
5061
5246
|
try {
|
|
@@ -5571,12 +5756,14 @@ async function updateProjectSkills(options = {}) {
|
|
|
5571
5756
|
const safeName = sanitizeMetadata(skill.name);
|
|
5572
5757
|
console.log(`${TEXT$1}Updating ${safeName}...${RESET$1}`);
|
|
5573
5758
|
const installUrl = formatSourceInput(skill.entry.source, skill.entry.ref);
|
|
5759
|
+
const subagentArgs = skill.entry.subagents?.length ? ["--subagent", ...skill.entry.subagents.map((s) => s === "" ? "root" : s)] : [];
|
|
5574
5760
|
if (spawnSync(process.execPath, [
|
|
5575
5761
|
cliEntry,
|
|
5576
5762
|
"add",
|
|
5577
5763
|
installUrl,
|
|
5578
5764
|
"--skill",
|
|
5579
5765
|
skill.name,
|
|
5766
|
+
...subagentArgs,
|
|
5580
5767
|
"-y"
|
|
5581
5768
|
], {
|
|
5582
5769
|
stdio: [
|
|
@@ -6084,6 +6271,9 @@ ${BOLD}Manage Skills:${RESET}
|
|
|
6084
6271
|
list, ls List installed skills
|
|
6085
6272
|
find [query] Search for skills interactively
|
|
6086
6273
|
|
|
6274
|
+
${BOLD}Find Options:${RESET}
|
|
6275
|
+
--owner <owner> Search only repositories from a GitHub owner
|
|
6276
|
+
|
|
6087
6277
|
${BOLD}Updates:${RESET}
|
|
6088
6278
|
update [skills...] Update skills to latest versions (alias: upgrade)
|
|
6089
6279
|
|
|
@@ -6104,6 +6294,7 @@ ${BOLD}Add Options:${RESET}
|
|
|
6104
6294
|
-l, --list List available skills in the repository without installing
|
|
6105
6295
|
-y, --yes Skip confirmation prompts
|
|
6106
6296
|
--copy Copy files instead of symlinking to agent directories
|
|
6297
|
+
--subagent <names> Install to Eve subagents (use 'root' for the root agent)
|
|
6107
6298
|
--all Shorthand for --skill '*' --agent '*' -y
|
|
6108
6299
|
--full-depth Search all subdirectories even when a root SKILL.md exists
|
|
6109
6300
|
|
|
@@ -6150,6 +6341,7 @@ ${BOLD}Examples:${RESET}
|
|
|
6150
6341
|
${DIM}$${RESET} skills ls --json ${DIM}# JSON output${RESET}
|
|
6151
6342
|
${DIM}$${RESET} skills find ${DIM}# interactive search${RESET}
|
|
6152
6343
|
${DIM}$${RESET} skills find typescript ${DIM}# search by keyword${RESET}
|
|
6344
|
+
${DIM}$${RESET} skills find react --owner vercel ${DIM}# search within an owner${RESET}
|
|
6153
6345
|
${DIM}$${RESET} skills update
|
|
6154
6346
|
${DIM}$${RESET} skills update my-skill ${DIM}# update a single skill${RESET}
|
|
6155
6347
|
${DIM}$${RESET} skills update -g ${DIM}# update global skills only${RESET}
|