skills 1.5.12 → 1.5.14

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.
Files changed (3) hide show
  1. package/README.md +3 -0
  2. package/dist/cli.mjs +253 -64
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -139,6 +139,9 @@ npx skills find
139
139
 
140
140
  # Search by keyword
141
141
  npx skills find typescript
142
+
143
+ # Search across every repository owned by an organization or user
144
+ npx skills find react --owner vercel
142
145
  ```
143
146
 
144
147
  ### `skills update`
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 getAgentBaseDir(agentType, global, cwd) {
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$1("sha256").update(bytes).digest("hex")}`;
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,23 +3132,24 @@ 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" || skill.mdPath.slice(0, -9);
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: download.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.12";
3150
+ var version$1 = "1.5.14";
3118
3151
  const isCancelled$1 = (value) => typeof value === "symbol";
3152
+ const EVE_AGENT_LABEL = "eve agent";
3119
3153
  async function isSourcePrivate(source) {
3120
3154
  const ownerRepo = parseOwnerRepo(source);
3121
3155
  if (!ownerRepo) return false;
@@ -3175,12 +3209,25 @@ function shortenPath$2(fullPath, cwd) {
3175
3209
  if (fullPath === cwd || fullPath.startsWith(cwd + sep)) return "." + fullPath.slice(cwd.length);
3176
3210
  return fullPath;
3177
3211
  }
3212
+ function computeSingleFileSkillHash(contents) {
3213
+ const hash = createHash("sha256");
3214
+ hash.update("SKILL.md");
3215
+ hash.update(contents);
3216
+ return hash.digest("hex");
3217
+ }
3178
3218
  function formatList$1(items, maxShow = 5) {
3179
3219
  if (items.length <= maxShow) return items.join(", ");
3180
3220
  const shown = items.slice(0, maxShow);
3181
3221
  const remaining = items.length - maxShow;
3182
3222
  return `${shown.join(", ")} +${remaining} more`;
3183
3223
  }
3224
+ function formatSkillPromptSubject(skills) {
3225
+ const namedSubject = formatList$1(skills.map((skill) => import_picocolors.default.cyan(getSkillDisplayName(skill))), 3);
3226
+ return stripTerminalEscapes(namedSubject).length <= 80 ? namedSubject : `${skills.length} selected skills`;
3227
+ }
3228
+ function formatEveInstallPromptMessage(skills) {
3229
+ return `Detected an eve project. Install ${formatSkillPromptSubject(skills)} for your ${EVE_AGENT_LABEL} to use?`;
3230
+ }
3184
3231
  function splitAgentsByType(agentTypes) {
3185
3232
  const universal = [];
3186
3233
  const symlinked = [];
@@ -3203,6 +3250,37 @@ function buildAgentSummaryLines(targetAgents, installMode) {
3203
3250
  }
3204
3251
  return lines;
3205
3252
  }
3253
+ function targetDisplayName(target) {
3254
+ const base = agents[target.agent].displayName;
3255
+ return target.subagent ? `${base} (${target.subagent})` : base;
3256
+ }
3257
+ function targetKey(target) {
3258
+ return target.subagent ? `${target.agent}:${target.subagent}` : target.agent;
3259
+ }
3260
+ function buildInstallTargets(targetAgents, eveSubagentTargets) {
3261
+ const targets = [];
3262
+ for (const agent of targetAgents) if (agent === "eve") for (const subagent of eveSubagentTargets) targets.push({
3263
+ agent,
3264
+ subagent
3265
+ });
3266
+ else targets.push({ agent });
3267
+ return targets;
3268
+ }
3269
+ function buildTargetSummaryLines(targets, installMode) {
3270
+ const lines = [];
3271
+ const rootAgents = targets.filter((t) => !t.subagent).map((t) => t.agent);
3272
+ const subagentNames = targets.filter((t) => t.subagent).map(targetDisplayName);
3273
+ const { universal, symlinked } = splitAgentsByType(rootAgents);
3274
+ if (installMode === "symlink") {
3275
+ if (universal.length > 0) lines.push(` ${import_picocolors.default.green("universal:")} ${formatList$1(universal)}`);
3276
+ if (symlinked.length > 0) lines.push(` ${import_picocolors.default.dim("symlink →")} ${formatList$1(symlinked)}`);
3277
+ if (subagentNames.length > 0) lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$1(subagentNames)}`);
3278
+ } else {
3279
+ const allNames = targets.map(targetDisplayName);
3280
+ lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$1(allNames)}`);
3281
+ }
3282
+ return lines;
3283
+ }
3206
3284
  function ensureUniversalAgents(targetAgents) {
3207
3285
  const universalAgents = getUniversalAgents();
3208
3286
  const result = [...targetAgents];
@@ -3617,16 +3695,6 @@ async function runAdd(args, options = {}) {
3617
3695
  if (!ownerRepo) return Promise.resolve(null);
3618
3696
  return isRepoPrivate(ownerRepo.owner, ownerRepo.repo).catch(() => null);
3619
3697
  })();
3620
- if (ownerRepoRaw?.split("/")[0]?.toLowerCase() === "openclaw" && !options.dangerouslyAcceptOpenclawRisks) {
3621
- console.log();
3622
- M.warn(import_picocolors.default.yellow(import_picocolors.default.bold("⚠ OpenClaw skills are unverified community submissions.")));
3623
- M.message(import_picocolors.default.yellow("This source contains user-submitted skills that have not been reviewed for safety or quality."));
3624
- M.message(import_picocolors.default.yellow("Skills run with full agent permissions and could be malicious."));
3625
- console.log();
3626
- M.message(`If you understand the risks, re-run with:\n\n ${import_picocolors.default.cyan(`npx skills add ${source} --dangerously-accept-openclaw-risks`)}\n`);
3627
- Se(import_picocolors.default.red("Installation blocked"));
3628
- process.exit(1);
3629
- }
3630
3698
  if (parsed.type === "well-known") {
3631
3699
  await handleWellKnownSkills(source, parsed.url, options, spinner);
3632
3700
  return;
@@ -3820,7 +3888,7 @@ async function runAdd(args, options = {}) {
3820
3888
  spinner.stop(`${totalAgents} agents`);
3821
3889
  if (installedAgents.includes("eve") && (options.yes || !agentResult.isAgent)) {
3822
3890
  const useEve = options.yes ? true : await ye({
3823
- message: "Detected an Eve project. Install skills for Eve?",
3891
+ message: formatEveInstallPromptMessage(selectedSkills),
3824
3892
  initialValue: true
3825
3893
  });
3826
3894
  if (pD(useEve)) {
@@ -3830,7 +3898,7 @@ async function runAdd(args, options = {}) {
3830
3898
  }
3831
3899
  if (useEve) {
3832
3900
  targetAgents = ["eve"];
3833
- M.info(`Installing to: ${import_picocolors.default.cyan(agents.eve.displayName)}`);
3901
+ M.info(`Installing to: ${import_picocolors.default.cyan(EVE_AGENT_LABEL)}`);
3834
3902
  } else {
3835
3903
  const selected = await selectAgentsInteractive({ global: options.global });
3836
3904
  if (pD(selected)) {
@@ -3872,6 +3940,35 @@ async function runAdd(args, options = {}) {
3872
3940
  targetAgents = selected;
3873
3941
  }
3874
3942
  }
3943
+ if (options.subagent && options.subagent.length > 0 && !targetAgents.includes("eve")) targetAgents = [...targetAgents, "eve"];
3944
+ let eveSubagentTargets = [void 0];
3945
+ if (targetAgents.includes("eve")) {
3946
+ const availableSubagents = getEveSubagents(process.cwd());
3947
+ if (options.subagent && options.subagent.length > 0) eveSubagentTargets = options.subagent.map((s) => s === "root" || s === "." ? void 0 : s);
3948
+ else if (availableSubagents.length > 0 && !options.yes) {
3949
+ const selectedSubagents = await fe({
3950
+ message: "Where should Eve skills be installed?",
3951
+ options: [{
3952
+ value: "",
3953
+ label: "Root agent",
3954
+ hint: "agent/skills"
3955
+ }, ...availableSubagents.map((name) => ({
3956
+ value: name,
3957
+ label: name,
3958
+ hint: `agent/subagents/${name}/skills`
3959
+ }))],
3960
+ initialValues: [""],
3961
+ required: true
3962
+ });
3963
+ if (pD(selectedSubagents)) {
3964
+ xe("Installation cancelled");
3965
+ await cleanup(tempDir);
3966
+ process.exit(0);
3967
+ }
3968
+ eveSubagentTargets = selectedSubagents.map((s) => s === "" ? void 0 : s);
3969
+ }
3970
+ }
3971
+ const installTargets = buildInstallTargets(targetAgents, eveSubagentTargets);
3875
3972
  let installGlobally = options.global ?? false;
3876
3973
  const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
3877
3974
  if (options.global === void 0 && !options.yes && supportsGlobal) {
@@ -3895,8 +3992,9 @@ async function runAdd(args, options = {}) {
3895
3992
  installGlobally = scope;
3896
3993
  }
3897
3994
  let installMode = options.copy ? "copy" : "symlink";
3898
- const uniqueDirs = new Set(targetAgents.map((a) => agents[a].skillsDir));
3899
- if (!options.copy && !options.yes && uniqueDirs.size > 1) {
3995
+ const allEve = installTargets.every((t) => t.agent === "eve");
3996
+ const uniqueDirs = new Set(installTargets.map((t) => t.subagent ? `eve:subagent:${t.subagent}` : agents[t.agent].skillsDir));
3997
+ if (!options.copy && !options.yes && uniqueDirs.size > 1 && !allEve) {
3900
3998
  const modeChoice = await ve({
3901
3999
  message: "Installation method",
3902
4000
  options: [{
@@ -3915,19 +4013,21 @@ async function runAdd(args, options = {}) {
3915
4013
  process.exit(0);
3916
4014
  }
3917
4015
  installMode = modeChoice;
3918
- } else if (uniqueDirs.size <= 1) installMode = "copy";
4016
+ } else if (uniqueDirs.size <= 1 || allEve) installMode = "copy";
3919
4017
  const cwd = process.cwd();
3920
4018
  const summaryLines = [];
3921
- targetAgents.map((a) => agents[a].displayName);
3922
- const overwriteChecks = await Promise.all(selectedSkills.flatMap((skill) => targetAgents.map(async (agent) => ({
4019
+ const overwriteChecks = await Promise.all(selectedSkills.flatMap((skill) => installTargets.map(async (target) => ({
3923
4020
  skillName: skill.name,
3924
- agent,
3925
- installed: await isSkillInstalled(skill.name, agent, { global: installGlobally })
4021
+ target,
4022
+ installed: await isSkillInstalled(skill.name, target.agent, {
4023
+ global: installGlobally,
4024
+ eveSubagent: target.subagent
4025
+ })
3926
4026
  }))));
3927
4027
  const overwriteStatus = /* @__PURE__ */ new Map();
3928
- for (const { skillName, agent, installed } of overwriteChecks) {
4028
+ for (const { skillName, target, installed } of overwriteChecks) {
3929
4029
  if (!overwriteStatus.has(skillName)) overwriteStatus.set(skillName, /* @__PURE__ */ new Map());
3930
- overwriteStatus.get(skillName).set(agent, installed);
4030
+ overwriteStatus.get(skillName).set(targetKey(target), installed);
3931
4031
  }
3932
4032
  const groupedSummary = {};
3933
4033
  const ungroupedSummary = [];
@@ -3939,14 +4039,15 @@ async function runAdd(args, options = {}) {
3939
4039
  const printSkillSummary = (skills) => {
3940
4040
  for (const skill of skills) {
3941
4041
  if (summaryLines.length > 0) summaryLines.push("");
3942
- const shortCanonical = shortenPath$2(targetAgents.length === 1 ? getCanonicalPath(skill.name, {
4042
+ const shortCanonical = shortenPath$2(installTargets.length === 1 ? getCanonicalPath(skill.name, {
3943
4043
  global: installGlobally,
3944
- agent: targetAgents[0]
4044
+ agent: installTargets[0].agent,
4045
+ eveSubagent: installTargets[0].subagent
3945
4046
  }) : getCanonicalPath(skill.name, { global: installGlobally }), cwd);
3946
4047
  summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
3947
- summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
4048
+ summaryLines.push(...buildTargetSummaryLines(installTargets, installMode));
3948
4049
  const skillOverwrites = overwriteStatus.get(skill.name);
3949
- const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
4050
+ const overwriteAgents = installTargets.filter((t) => skillOverwrites?.get(targetKey(t))).map(targetDisplayName);
3950
4051
  if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
3951
4052
  }
3952
4053
  };
@@ -3986,7 +4087,8 @@ async function runAdd(args, options = {}) {
3986
4087
  }
3987
4088
  spinner.start("Installing skills...");
3988
4089
  const results = [];
3989
- for (const skill of selectedSkills) for (const agent of targetAgents) {
4090
+ for (const skill of selectedSkills) for (const target of installTargets) {
4091
+ const { agent, subagent } = target;
3990
4092
  let result;
3991
4093
  if (blobResult && "files" in skill) {
3992
4094
  const blobSkill = skill;
@@ -3995,15 +4097,27 @@ async function runAdd(args, options = {}) {
3995
4097
  files: blobSkill.files
3996
4098
  }, agent, {
3997
4099
  global: installGlobally,
3998
- mode: installMode
4100
+ mode: installMode,
4101
+ eveSubagent: subagent
3999
4102
  });
4000
- } else result = await installSkillForAgent(skill, agent, {
4103
+ } else if (tempDir && skill.path === tempDir && skill.rawContent) result = await installBlobSkillForAgent({
4104
+ installName: skill.name,
4105
+ files: [{
4106
+ path: "SKILL.md",
4107
+ contents: skill.rawContent
4108
+ }]
4109
+ }, agent, {
4001
4110
  global: installGlobally,
4002
4111
  mode: installMode
4003
4112
  });
4113
+ else result = await installSkillForAgent(skill, agent, {
4114
+ global: installGlobally,
4115
+ mode: installMode,
4116
+ eveSubagent: subagent
4117
+ });
4004
4118
  results.push({
4005
4119
  skill: getSkillDisplayName(skill),
4006
- agent: agents[agent].displayName,
4120
+ agent: targetDisplayName(target),
4007
4121
  pluginName: skill.pluginName,
4008
4122
  ...result
4009
4123
  });
@@ -4069,17 +4183,20 @@ async function runAdd(args, options = {}) {
4069
4183
  }
4070
4184
  if (successful.length > 0 && !installGlobally) {
4071
4185
  const successfulSkillNames = new Set(successful.map((r) => r.skill));
4186
+ const eveSubagents = targetAgents.includes("eve") ? eveSubagentTargets.map((s) => s ?? "") : void 0;
4187
+ const recordSubagents = eveSubagents && (eveSubagents.length > 1 || eveSubagents.some((s) => s !== ""));
4072
4188
  for (const skill of selectedSkills) {
4073
4189
  const skillDisplayName = getSkillDisplayName(skill);
4074
4190
  if (successfulSkillNames.has(skillDisplayName)) try {
4075
- const computedHash = blobResult && "snapshotHash" in skill ? skill.snapshotHash : await computeSkillFolderHash(skill.path);
4191
+ const computedHash = blobResult && "snapshotHash" in skill ? skill.snapshotHash : tempDir && skill.path === tempDir && skill.rawContent ? computeSingleFileSkillHash(skill.rawContent) : await computeSkillFolderHash(skill.path);
4076
4192
  const skillPathValue = skillFiles[skill.name];
4077
4193
  await addSkillToLocalLock(skill.name, {
4078
4194
  source: lockSource || parsed.url,
4079
4195
  ref: parsed.ref,
4080
4196
  sourceType: parsed.type,
4081
4197
  ...skillPathValue && { skillPath: skillPathValue },
4082
- computedHash
4198
+ computedHash,
4199
+ ...recordSubagents && { subagents: eveSubagents }
4083
4200
  }, cwd);
4084
4201
  } catch {}
4085
4202
  }
@@ -4237,8 +4354,17 @@ function parseAddOptions(args) {
4237
4354
  i--;
4238
4355
  } else if (arg === "--full-depth") options.fullDepth = true;
4239
4356
  else if (arg === "--copy") options.copy = true;
4240
- else if (arg === "--dangerously-accept-openclaw-risks") options.dangerouslyAcceptOpenclawRisks = true;
4241
- else if (arg && !arg.startsWith("-")) source.push(arg);
4357
+ else if (arg === "--subagent") {
4358
+ options.subagent = options.subagent || [];
4359
+ i++;
4360
+ let nextArg = args[i];
4361
+ while (i < args.length && nextArg && !nextArg.startsWith("-")) {
4362
+ options.subagent.push(nextArg);
4363
+ i++;
4364
+ nextArg = args[i];
4365
+ }
4366
+ i--;
4367
+ } else if (arg && !arg.startsWith("-")) source.push(arg);
4242
4368
  }
4243
4369
  return {
4244
4370
  source,
@@ -4257,9 +4383,54 @@ function formatInstalls(count) {
4257
4383
  if (count >= 1e3) return `${(count / 1e3).toFixed(1).replace(/\.0$/, "")}K installs`;
4258
4384
  return `${count} install${count === 1 ? "" : "s"}`;
4259
4385
  }
4260
- async function searchSkillsAPI(query) {
4386
+ const GITHUB_OWNER_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,38})$/i;
4387
+ function parseFindOptions(args) {
4388
+ const queryParts = [];
4389
+ const options = {};
4390
+ const errors = [];
4391
+ for (let i = 0; i < args.length; i++) {
4392
+ const arg = args[i];
4393
+ if (!arg) continue;
4394
+ let ownerValue;
4395
+ if (arg === "--owner") {
4396
+ const value = args[i + 1];
4397
+ if (!value || value.startsWith("-")) {
4398
+ errors.push("--owner requires a GitHub owner");
4399
+ continue;
4400
+ }
4401
+ ownerValue = value;
4402
+ i++;
4403
+ } else if (arg.startsWith("--owner=")) {
4404
+ ownerValue = arg.slice(8);
4405
+ if (!ownerValue) {
4406
+ errors.push("--owner requires a GitHub owner");
4407
+ continue;
4408
+ }
4409
+ } else {
4410
+ queryParts.push(arg);
4411
+ continue;
4412
+ }
4413
+ const owner = ownerValue.trim().toLowerCase();
4414
+ if (!GITHUB_OWNER_PATTERN.test(owner)) {
4415
+ errors.push("--owner must be a valid GitHub owner");
4416
+ continue;
4417
+ }
4418
+ options.owner = owner;
4419
+ }
4420
+ return {
4421
+ query: queryParts.join(" "),
4422
+ options,
4423
+ errors
4424
+ };
4425
+ }
4426
+ async function searchSkillsAPI(query, owner) {
4261
4427
  try {
4262
- const url = `${SEARCH_API_BASE}/api/search?q=${encodeURIComponent(query)}&limit=10`;
4428
+ const params = new URLSearchParams({
4429
+ q: query,
4430
+ limit: "10"
4431
+ });
4432
+ if (owner) params.set("owner", owner);
4433
+ const url = `${SEARCH_API_BASE}/api/search?${params.toString()}`;
4263
4434
  const res = await fetch(url);
4264
4435
  if (!res.ok) return [];
4265
4436
  return (await res.json()).skills.map((skill) => ({
@@ -4277,7 +4448,7 @@ const SHOW_CURSOR = "\x1B[?25h";
4277
4448
  const CLEAR_DOWN = "\x1B[J";
4278
4449
  const MOVE_UP = (n) => `\x1b[${n}A`;
4279
4450
  const MOVE_TO_COL = (n) => `\x1b[${n}G`;
4280
- async function runSearchPrompt(initialQuery = "") {
4451
+ async function runSearchPrompt(initialQuery = "", owner) {
4281
4452
  let results = [];
4282
4453
  let selectedIndex = 0;
4283
4454
  let query = initialQuery;
@@ -4334,7 +4505,7 @@ async function runSearchPrompt(initialQuery = "") {
4334
4505
  const debounceMs = Math.max(150, 350 - q.length * 50);
4335
4506
  debounceTimer = setTimeout(async () => {
4336
4507
  try {
4337
- results = await searchSkillsAPI(q);
4508
+ results = await searchSkillsAPI(q, owner);
4338
4509
  selectedIndex = 0;
4339
4510
  } catch {
4340
4511
  results = [];
@@ -4407,20 +4578,27 @@ async function isRepoPublic(owner, repo) {
4407
4578
  return await isRepoPrivate(owner, repo) === false;
4408
4579
  }
4409
4580
  async function runFind(args) {
4410
- const query = args.join(" ");
4581
+ const { query, options: findOptions, errors } = parseFindOptions(args);
4582
+ const owner = findOptions.owner;
4411
4583
  const isNonInteractive = !process.stdin.isTTY;
4412
4584
  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}
4585
+ ${DIM$3} 1) npx skills find [query] [--owner <owner>]${RESET$3}
4414
4586
  ${DIM$3} 2) npx skills add <owner/repo@skill>${RESET$3}`;
4587
+ if (errors.length > 0) {
4588
+ for (const error of errors) console.error(error);
4589
+ console.error("Usage: npx skills find <query> [--owner <owner>]");
4590
+ return;
4591
+ }
4415
4592
  if (query) {
4416
- const results = await searchSkillsAPI(query);
4593
+ const results = await searchSkillsAPI(query, owner);
4417
4594
  track({
4418
4595
  event: "find",
4419
4596
  query,
4420
4597
  resultCount: String(results.length)
4421
4598
  });
4422
4599
  if (results.length === 0) {
4423
- console.log(`${DIM$3}No skills found for "${query}"${RESET$3}`);
4600
+ const ownerSuffix = owner ? ` from owner "${owner}"` : "";
4601
+ console.log(`${DIM$3}No skills found for "${query}"${ownerSuffix}${RESET$3}`);
4424
4602
  return;
4425
4603
  }
4426
4604
  console.log(`${DIM$3}Install with${RESET$3} npx skills add <owner/repo@skill>`);
@@ -4437,10 +4615,10 @@ ${DIM$3} 2) npx skills add <owner/repo@skill>${RESET$3}`;
4437
4615
  if (isNonInteractive || await isRunningInAgent()) {
4438
4616
  console.log(agentTip);
4439
4617
  console.log();
4440
- console.log(`${DIM$3}Usage: npx skills find <query>${RESET$3}`);
4618
+ console.log(`${DIM$3}Usage: npx skills find <query> [--owner <owner>]${RESET$3}`);
4441
4619
  return;
4442
4620
  }
4443
- const selected = await runSearchPrompt();
4621
+ const selected = await runSearchPrompt("", owner);
4444
4622
  track({
4445
4623
  event: "find",
4446
4624
  query: "",
@@ -4457,12 +4635,12 @@ ${DIM$3} 2) npx skills add <owner/repo@skill>${RESET$3}`;
4457
4635
  console.log();
4458
4636
  console.log(`${TEXT$2}Installing ${BOLD$3}${skillName}${RESET$3} from ${DIM$3}${pkg}${RESET$3}...`);
4459
4637
  console.log();
4460
- const { source, options } = parseAddOptions([
4638
+ const { source, options: addOptions } = parseAddOptions([
4461
4639
  pkg,
4462
4640
  "--skill",
4463
4641
  skillName
4464
4642
  ]);
4465
- await runAdd(source, options);
4643
+ await runAdd(source, addOptions);
4466
4644
  console.log();
4467
4645
  const info = getOwnerRepoFromString(pkg);
4468
4646
  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 +5160,7 @@ async function removeCommand(skillNames, options) {
4982
5160
  } else {
4983
5161
  await scanDir(getCanonicalSkillsDir(false, cwd));
4984
5162
  for (const agent of Object.values(agents)) await scanDir(join(cwd, agent.skillsDir));
5163
+ for (const subagent of getEveSubagents(cwd)) await scanDir(getEveSubagentSkillsDir(subagent, cwd));
4985
5164
  }
4986
5165
  const installedSkills = Array.from(skillNamesSet).sort();
4987
5166
  spinner.stop(`Found ${installedSkills.length} unique installed skill(s)`);
@@ -5055,7 +5234,10 @@ async function removeCommand(skillNames, options) {
5055
5234
  const pathsToCleanup = new Set([skillPath]);
5056
5235
  const sanitizedName = sanitizeName(skillName);
5057
5236
  if (isGlobal && agent.globalSkillsDir) pathsToCleanup.add(join(agent.globalSkillsDir, sanitizedName));
5058
- else pathsToCleanup.add(join(cwd, agent.skillsDir, sanitizedName));
5237
+ else {
5238
+ pathsToCleanup.add(join(cwd, agent.skillsDir, sanitizedName));
5239
+ if (agentKey === "eve") for (const subagent of getEveSubagents(cwd)) pathsToCleanup.add(join(getEveSubagentSkillsDir(subagent, cwd), sanitizedName));
5240
+ }
5059
5241
  for (const pathToCleanup of pathsToCleanup) {
5060
5242
  if (pathToCleanup === canonicalPath) continue;
5061
5243
  try {
@@ -5571,12 +5753,14 @@ async function updateProjectSkills(options = {}) {
5571
5753
  const safeName = sanitizeMetadata(skill.name);
5572
5754
  console.log(`${TEXT$1}Updating ${safeName}...${RESET$1}`);
5573
5755
  const installUrl = formatSourceInput(skill.entry.source, skill.entry.ref);
5756
+ const subagentArgs = skill.entry.subagents?.length ? ["--subagent", ...skill.entry.subagents.map((s) => s === "" ? "root" : s)] : [];
5574
5757
  if (spawnSync(process.execPath, [
5575
5758
  cliEntry,
5576
5759
  "add",
5577
5760
  installUrl,
5578
5761
  "--skill",
5579
5762
  skill.name,
5763
+ ...subagentArgs,
5580
5764
  "-y"
5581
5765
  ], {
5582
5766
  stdio: [
@@ -6084,6 +6268,9 @@ ${BOLD}Manage Skills:${RESET}
6084
6268
  list, ls List installed skills
6085
6269
  find [query] Search for skills interactively
6086
6270
 
6271
+ ${BOLD}Find Options:${RESET}
6272
+ --owner <owner> Search only repositories from a GitHub owner
6273
+
6087
6274
  ${BOLD}Updates:${RESET}
6088
6275
  update [skills...] Update skills to latest versions (alias: upgrade)
6089
6276
 
@@ -6104,6 +6291,7 @@ ${BOLD}Add Options:${RESET}
6104
6291
  -l, --list List available skills in the repository without installing
6105
6292
  -y, --yes Skip confirmation prompts
6106
6293
  --copy Copy files instead of symlinking to agent directories
6294
+ --subagent <names> Install to Eve subagents (use 'root' for the root agent)
6107
6295
  --all Shorthand for --skill '*' --agent '*' -y
6108
6296
  --full-depth Search all subdirectories even when a root SKILL.md exists
6109
6297
 
@@ -6150,6 +6338,7 @@ ${BOLD}Examples:${RESET}
6150
6338
  ${DIM}$${RESET} skills ls --json ${DIM}# JSON output${RESET}
6151
6339
  ${DIM}$${RESET} skills find ${DIM}# interactive search${RESET}
6152
6340
  ${DIM}$${RESET} skills find typescript ${DIM}# search by keyword${RESET}
6341
+ ${DIM}$${RESET} skills find react --owner vercel ${DIM}# search within an owner${RESET}
6153
6342
  ${DIM}$${RESET} skills update
6154
6343
  ${DIM}$${RESET} skills update my-skill ${DIM}# update a single skill${RESET}
6155
6344
  ${DIM}$${RESET} skills update -g ${DIM}# update global skills only${RESET}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skills",
3
- "version": "1.5.12",
3
+ "version": "1.5.14",
4
4
  "description": "The open agent skills ecosystem",
5
5
  "type": "module",
6
6
  "bin": {