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.
Files changed (3) hide show
  1. package/README.md +3 -0
  2. package/dist/cli.mjs +243 -51
  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,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" || 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.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 uniqueDirs = new Set(targetAgents.map((a) => agents[a].skillsDir));
3899
- if (!options.copy && !options.yes && uniqueDirs.size > 1) {
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
- targetAgents.map((a) => agents[a].displayName);
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
- agent,
3925
- installed: await isSkillInstalled(skill.name, agent, { global: installGlobally })
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, agent, installed } of overwriteChecks) {
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(agent, installed);
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(targetAgents.length === 1 ? getCanonicalPath(skill.name, {
4044
+ const shortCanonical = shortenPath$2(installTargets.length === 1 ? getCanonicalPath(skill.name, {
3943
4045
  global: installGlobally,
3944
- agent: targetAgents[0]
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(...buildAgentSummaryLines(targetAgents, installMode));
4050
+ summaryLines.push(...buildTargetSummaryLines(installTargets, installMode));
3948
4051
  const skillOverwrites = overwriteStatus.get(skill.name);
3949
- const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
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 agent of targetAgents) {
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 installSkillForAgent(skill, agent, {
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: agents[agent].displayName,
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 === "--dangerously-accept-openclaw-risks") options.dangerouslyAcceptOpenclawRisks = true;
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
- async function searchSkillsAPI(query) {
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 url = `${SEARCH_API_BASE}/api/search?q=${encodeURIComponent(query)}&limit=10`;
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.join(" ");
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
- console.log(`${DIM$3}No skills found for "${query}"${RESET$3}`);
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>${RESET$3}`);
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, options);
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 pathsToCleanup.add(join(cwd, agent.skillsDir, sanitizedName));
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}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skills",
3
- "version": "1.5.12",
3
+ "version": "1.5.13",
4
4
  "description": "The open agent skills ecosystem",
5
5
  "type": "module",
6
6
  "bin": {