skills 1.5.25 → 1.5.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +173 -48
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import { Writable } from "stream";
|
|
|
18
18
|
import { promisify } from "util";
|
|
19
19
|
import { execFile, spawn, spawnSync } from "child_process";
|
|
20
20
|
import { access, chmod, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rm, stat, symlink, writeFile } from "fs/promises";
|
|
21
|
-
import { parse } from "yaml";
|
|
21
|
+
import { parse, stringify } from "yaml";
|
|
22
22
|
import { createHash } from "crypto";
|
|
23
23
|
import { mkdir as mkdir$1, mkdtemp as mkdtemp$1, readFile as readFile$1, rm as rm$1, stat as stat$1, writeFile as writeFile$1 } from "node:fs/promises";
|
|
24
24
|
import { createHash as createHash$1 } from "node:crypto";
|
|
@@ -1464,6 +1464,7 @@ const agents = {
|
|
|
1464
1464
|
displayName: "Claude Code",
|
|
1465
1465
|
skillsDir: ".claude/skills",
|
|
1466
1466
|
globalSkillsDir: join(claudeHome, "skills"),
|
|
1467
|
+
createProjectSkillsDirByDefault: true,
|
|
1467
1468
|
detectInstalled: async () => {
|
|
1468
1469
|
return existsSync(claudeHome);
|
|
1469
1470
|
}
|
|
@@ -2145,6 +2146,11 @@ function isPathSafe$2(basePath, targetPath) {
|
|
|
2145
2146
|
function pathsOverlap(pathA, pathB) {
|
|
2146
2147
|
return isPathSafe$2(pathA, pathB) || isPathSafe$2(pathB, pathA);
|
|
2147
2148
|
}
|
|
2149
|
+
function shouldSkipProjectAgentSymlink(agentType, isGlobal, cwd, createMissingAgentRoot) {
|
|
2150
|
+
if (isGlobal || isUniversalAgent(agentType) || createMissingAgentRoot || agents[agentType].createProjectSkillsDirByDefault) return false;
|
|
2151
|
+
const agentRoot = agents[agentType].skillsDir.split("/")[0];
|
|
2152
|
+
return !existsSync(join(cwd, agentRoot));
|
|
2153
|
+
}
|
|
2148
2154
|
async function isDirEntryOrSymlinkToDir(entry, entryPath) {
|
|
2149
2155
|
if (entry.isDirectory()) return true;
|
|
2150
2156
|
if (!entry.isSymbolicLink()) return false;
|
|
@@ -2280,15 +2286,14 @@ async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
2280
2286
|
canonicalPath: canonicalDir,
|
|
2281
2287
|
mode: "symlink"
|
|
2282
2288
|
};
|
|
2283
|
-
if (
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
}
|
|
2289
|
+
if (shouldSkipProjectAgentSymlink(agentType, isGlobal, cwd, options.createMissingAgentRoot ?? false)) return {
|
|
2290
|
+
success: true,
|
|
2291
|
+
path: canonicalDir,
|
|
2292
|
+
canonicalPath: canonicalDir,
|
|
2293
|
+
mode: "symlink",
|
|
2294
|
+
skipped: true,
|
|
2295
|
+
skipReason: "missing-agent-project-directory"
|
|
2296
|
+
};
|
|
2292
2297
|
if (!await createSymlink(canonicalDir, agentDir)) {
|
|
2293
2298
|
await cleanAndCreateDirectory(agentDir);
|
|
2294
2299
|
await copyDirectory(skill.path, agentDir, agentType);
|
|
@@ -2329,15 +2334,14 @@ const isExcluded$1 = (name, isDirectory = false) => {
|
|
|
2329
2334
|
function stripIgnoredEveFrontmatter(raw) {
|
|
2330
2335
|
const { data, content } = parseFrontmatter(raw);
|
|
2331
2336
|
const eveData = {};
|
|
2337
|
+
if (typeof data.name === "string") eveData.name = data.name;
|
|
2332
2338
|
if (typeof data.description === "string") eveData.description = data.description;
|
|
2333
2339
|
if (typeof data.license === "string") eveData.license = data.license;
|
|
2334
|
-
if (data.
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
if (keys.length === 0) return content.replace(/^\r?\n/u, "");
|
|
2340
|
-
return `---\n${keys.map((key) => `${key}: ${JSON.stringify(eveData[key])}`).join("\n")}\n---\n${content.replace(/^\r?\n/u, "")}`;
|
|
2340
|
+
if (data.compatibility !== void 0) eveData.compatibility = data.compatibility;
|
|
2341
|
+
if (typeof data.version === "string" || typeof data.version === "number") eveData.version = data.version;
|
|
2342
|
+
if (data.metadata && typeof data.metadata === "object" && !Array.isArray(data.metadata)) eveData.metadata = data.metadata;
|
|
2343
|
+
if (Object.keys(eveData).length === 0) return content.replace(/^\r?\n/u, "");
|
|
2344
|
+
return `---\n${stringify(eveData).trimEnd()}\n---\n${content.replace(/^\r?\n/u, "")}`;
|
|
2341
2345
|
}
|
|
2342
2346
|
async function copyDirectory(src, dest, agentType) {
|
|
2343
2347
|
await mkdir(dest, { recursive: true });
|
|
@@ -2571,15 +2575,14 @@ async function installBlobSkillForAgent(skill, agentType, options = {}) {
|
|
|
2571
2575
|
canonicalPath: canonicalDir,
|
|
2572
2576
|
mode: "symlink"
|
|
2573
2577
|
};
|
|
2574
|
-
if (
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
}
|
|
2578
|
+
if (shouldSkipProjectAgentSymlink(agentType, isGlobal, cwd, options.createMissingAgentRoot ?? false)) return {
|
|
2579
|
+
success: true,
|
|
2580
|
+
path: canonicalDir,
|
|
2581
|
+
canonicalPath: canonicalDir,
|
|
2582
|
+
mode: "symlink",
|
|
2583
|
+
skipped: true,
|
|
2584
|
+
skipReason: "missing-agent-project-directory"
|
|
2585
|
+
};
|
|
2583
2586
|
if (!await createSymlink(canonicalDir, agentDir)) {
|
|
2584
2587
|
await cleanAndCreateDirectory(agentDir);
|
|
2585
2588
|
await writeSkillFiles(agentDir);
|
|
@@ -4121,7 +4124,7 @@ async function tryBlobInstall(ownerRepo, options = {}) {
|
|
|
4121
4124
|
tree
|
|
4122
4125
|
};
|
|
4123
4126
|
}
|
|
4124
|
-
var version$1 = "1.5.
|
|
4127
|
+
var version$1 = "1.5.26";
|
|
4125
4128
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
4126
4129
|
const EVE_AGENT_LABEL = "eve agent";
|
|
4127
4130
|
async function isSourcePrivate(source) {
|
|
@@ -4269,9 +4272,11 @@ function buildResultLines(results, targetAgents) {
|
|
|
4269
4272
|
const { universal, symlinked: symlinkAgents } = splitAgentsByType(targetAgents);
|
|
4270
4273
|
const successfulSymlinks = results.filter((r) => !r.symlinkFailed && !r.skipped && !universal.includes(r.agent)).map((r) => r.agent);
|
|
4271
4274
|
const failedSymlinks = results.filter((r) => r.symlinkFailed && !r.skipped).map((r) => r.agent);
|
|
4275
|
+
const skippedSymlinks = results.filter((r) => r.skipped && r.skipReason === "missing-agent-project-directory" && symlinkAgents.includes(r.agent)).map((r) => r.agent);
|
|
4272
4276
|
if (universal.length > 0) lines.push(` ${import_picocolors.default.green("universal:")} ${formatList$1(universal)}`);
|
|
4273
4277
|
if (successfulSymlinks.length > 0) lines.push(` ${import_picocolors.default.dim("symlinked:")} ${formatList$1(successfulSymlinks)}`);
|
|
4274
4278
|
if (failedSymlinks.length > 0) lines.push(` ${import_picocolors.default.yellow("copied:")} ${formatList$1(failedSymlinks)}`);
|
|
4279
|
+
if (skippedSymlinks.length > 0) lines.push(` ${import_picocolors.default.yellow("skipped:")} ${formatList$1(skippedSymlinks)} ${import_picocolors.default.dim("(project directory not found)")}`);
|
|
4275
4280
|
return lines;
|
|
4276
4281
|
}
|
|
4277
4282
|
function exitInstallationCancelled() {
|
|
@@ -4341,6 +4346,17 @@ async function selectAgentsInteractive(options) {
|
|
|
4341
4346
|
return selected;
|
|
4342
4347
|
}
|
|
4343
4348
|
setVersion(version$1);
|
|
4349
|
+
function buildJsonSecurity(auditData, skillName, source) {
|
|
4350
|
+
const data = auditData?.[skillName];
|
|
4351
|
+
if (!data || Object.keys(data).length === 0) return null;
|
|
4352
|
+
const socketAlerts = data.socket?.alerts ?? 0;
|
|
4353
|
+
return {
|
|
4354
|
+
...data.ath && { gen: data.ath.risk },
|
|
4355
|
+
...data.socket && { socket: `${socketAlerts} alert${socketAlerts !== 1 ? "s" : ""}` },
|
|
4356
|
+
...data.snyk && { snyk: data.snyk.risk },
|
|
4357
|
+
...source && { details: `https://skills.sh/${source}` }
|
|
4358
|
+
};
|
|
4359
|
+
}
|
|
4344
4360
|
function isSkillsShPackUrl(url) {
|
|
4345
4361
|
try {
|
|
4346
4362
|
const parsed = new URL(url);
|
|
@@ -4544,6 +4560,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4544
4560
|
console.log();
|
|
4545
4561
|
const successful = results.filter((r) => r.success);
|
|
4546
4562
|
const failed = results.filter((r) => !r.success);
|
|
4563
|
+
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
4547
4564
|
const skillFiles = {};
|
|
4548
4565
|
for (const skill of selectedSkills) skillFiles[skill.installName] = skill.sourceUrl;
|
|
4549
4566
|
if (await wellKnownPrivacyPromise !== true) track({
|
|
@@ -4558,7 +4575,6 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4558
4575
|
sourceType: "well-known"
|
|
4559
4576
|
});
|
|
4560
4577
|
if (successful.length > 0 && installGlobally) {
|
|
4561
|
-
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
4562
4578
|
for (const skill of selectedSkills) if (successfulSkillNames.has(skill.installName)) try {
|
|
4563
4579
|
await addSkillToLock(skill.installName, {
|
|
4564
4580
|
source: sourceIdentifier,
|
|
@@ -4571,7 +4587,6 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4571
4587
|
} catch {}
|
|
4572
4588
|
}
|
|
4573
4589
|
if (successful.length > 0 && !installGlobally) {
|
|
4574
|
-
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
4575
4590
|
for (const skill of selectedSkills) if (successfulSkillNames.has(skill.installName)) try {
|
|
4576
4591
|
const matchingResult = successful.find((r) => r.skill === skill.installName);
|
|
4577
4592
|
const installDir = matchingResult?.canonicalPath || matchingResult?.path;
|
|
@@ -4638,6 +4653,40 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4638
4653
|
async function runAdd(args, options = {}) {
|
|
4639
4654
|
const source = args[0];
|
|
4640
4655
|
let installTipShown = false;
|
|
4656
|
+
const jsonMode = options.json === true;
|
|
4657
|
+
const jsonResults = [];
|
|
4658
|
+
const originalStdoutWrite = process.stdout.write;
|
|
4659
|
+
let stdoutSuppressed = false;
|
|
4660
|
+
if (jsonMode) {
|
|
4661
|
+
process.stdout.write = process.stderr.write.bind(process.stderr);
|
|
4662
|
+
stdoutSuppressed = true;
|
|
4663
|
+
}
|
|
4664
|
+
const restoreStdout = () => {
|
|
4665
|
+
if (stdoutSuppressed) {
|
|
4666
|
+
process.stdout.write = originalStdoutWrite;
|
|
4667
|
+
stdoutSuppressed = false;
|
|
4668
|
+
}
|
|
4669
|
+
};
|
|
4670
|
+
let jsonEmitted = false;
|
|
4671
|
+
const emitJson = () => {
|
|
4672
|
+
if (!jsonMode || jsonEmitted) return;
|
|
4673
|
+
jsonEmitted = true;
|
|
4674
|
+
restoreStdout();
|
|
4675
|
+
console.log(JSON.stringify(jsonResults, null, 2));
|
|
4676
|
+
};
|
|
4677
|
+
const emitJsonAndExit = (code, errorMessage) => {
|
|
4678
|
+
if (jsonMode) {
|
|
4679
|
+
if (errorMessage !== void 0) console.error(errorMessage);
|
|
4680
|
+
if (code !== 0 && jsonResults.length === 0) jsonResults.push({
|
|
4681
|
+
status: "failed",
|
|
4682
|
+
error: errorMessage ?? "Installation failed"
|
|
4683
|
+
});
|
|
4684
|
+
emitJson();
|
|
4685
|
+
}
|
|
4686
|
+
process.exit(code);
|
|
4687
|
+
};
|
|
4688
|
+
const emitJsonOnExit = () => emitJson();
|
|
4689
|
+
if (jsonMode) process.once("exit", emitJsonOnExit);
|
|
4641
4690
|
const showInstallTip = () => {
|
|
4642
4691
|
if (installTipShown) return;
|
|
4643
4692
|
log.message(import_picocolors.default.dim("Tip: use the --yes (-y) and --global (-g) flags to install without prompts."));
|
|
@@ -4653,8 +4702,9 @@ async function runAdd(args, options = {}) {
|
|
|
4653
4702
|
console.log(import_picocolors.default.dim(" Example:"));
|
|
4654
4703
|
console.log(` ${import_picocolors.default.cyan("npx skills add")} ${import_picocolors.default.yellow("vercel-labs/agent-skills")}`);
|
|
4655
4704
|
console.log();
|
|
4656
|
-
|
|
4705
|
+
emitJsonAndExit(1, "Missing required argument: source");
|
|
4657
4706
|
}
|
|
4707
|
+
const explicitlySelectedAgents = new Set(options.agent?.includes("*") ? [] : options.agent ?? []);
|
|
4658
4708
|
if (options.all) {
|
|
4659
4709
|
options.skill = ["*"];
|
|
4660
4710
|
options.agent = ["*"];
|
|
@@ -4668,13 +4718,19 @@ async function runAdd(args, options = {}) {
|
|
|
4668
4718
|
if (mappedAgent) options.agent = ensureUniversalAgents([mappedAgent]);
|
|
4669
4719
|
}
|
|
4670
4720
|
}
|
|
4721
|
+
if (jsonMode && !options.yes) emitJsonAndExit(1, "The --json flag requires --yes (or --all) to run non-interactively.");
|
|
4722
|
+
if (jsonMode && options.list) emitJsonAndExit(1, "The --json flag cannot be combined with --list.");
|
|
4671
4723
|
console.log();
|
|
4672
4724
|
if (!agentResult.isAgent) intro(import_picocolors.default.bgCyan(import_picocolors.default.black(" skills ")));
|
|
4673
4725
|
if (agentResult.isAgent) log.info(import_picocolors.default.bgCyan(import_picocolors.default.black(import_picocolors.default.bold(` ${agentResult.agent.name} `))) + " Agent detected — installing non-interactively");
|
|
4674
4726
|
else if (!process.stdin.isTTY) showInstallTip();
|
|
4675
4727
|
let tempDir = null;
|
|
4676
4728
|
try {
|
|
4677
|
-
const spinner$3 =
|
|
4729
|
+
const spinner$3 = jsonMode ? {
|
|
4730
|
+
start: () => {},
|
|
4731
|
+
stop: () => {},
|
|
4732
|
+
message: () => {}
|
|
4733
|
+
} : spinner();
|
|
4678
4734
|
spinner$3.start("Parsing source…");
|
|
4679
4735
|
const parsed = parseSource(source);
|
|
4680
4736
|
let directDownload = parsed.type === "download";
|
|
@@ -4688,6 +4744,7 @@ async function runAdd(args, options = {}) {
|
|
|
4688
4744
|
return isRepoPrivate(ownerRepo.owner, ownerRepo.repo).catch(() => null);
|
|
4689
4745
|
})();
|
|
4690
4746
|
if (parsed.type === "well-known") {
|
|
4747
|
+
if (jsonMode) emitJsonAndExit(1, "--json is not yet supported for well-known skill sources.");
|
|
4691
4748
|
if (await handleWellKnownSkills(source, parsed.url, options, spinner$3)) return;
|
|
4692
4749
|
directDownload = true;
|
|
4693
4750
|
}
|
|
@@ -4703,7 +4760,7 @@ async function runAdd(args, options = {}) {
|
|
|
4703
4760
|
if (!existsSync(parsed.localPath)) {
|
|
4704
4761
|
spinner$3.stop(import_picocolors.default.red("Path not found"));
|
|
4705
4762
|
outro(import_picocolors.default.red(`Local path does not exist: ${parsed.localPath}`));
|
|
4706
|
-
|
|
4763
|
+
emitJsonAndExit(1, `Local path does not exist: ${parsed.localPath}`);
|
|
4707
4764
|
}
|
|
4708
4765
|
spinner$3.stop("Local path validated");
|
|
4709
4766
|
spinner$3.start("Discovering skills…");
|
|
@@ -4722,15 +4779,18 @@ async function runAdd(args, options = {}) {
|
|
|
4722
4779
|
fullDepth: options.fullDepth
|
|
4723
4780
|
});
|
|
4724
4781
|
} else if (parsed.type === "github" && !options.fullDepth) {
|
|
4782
|
+
let attemptedBlobInstall = false;
|
|
4725
4783
|
const BLOB_ALLOWED_OWNERS = [
|
|
4726
4784
|
"vercel",
|
|
4727
4785
|
"vercel-labs",
|
|
4728
|
-
"heygen-com"
|
|
4786
|
+
"heygen-com",
|
|
4787
|
+
"remotion-dev"
|
|
4729
4788
|
];
|
|
4730
4789
|
const ownerRepo = getOwnerRepo(parsed);
|
|
4731
4790
|
const owner = ownerRepo?.split("/")[0]?.toLowerCase();
|
|
4732
4791
|
const isSelfHostedRepo = !!ownerRepo && Object.hasOwn(BLOB_ALLOWED_REPOS, ownerRepo.toLowerCase());
|
|
4733
4792
|
if (ownerRepo && owner && (isSelfHostedRepo || BLOB_ALLOWED_OWNERS.includes(owner))) {
|
|
4793
|
+
attemptedBlobInstall = true;
|
|
4734
4794
|
spinner$3.start("Fetching skills…");
|
|
4735
4795
|
blobResult = await tryBlobInstall(ownerRepo, {
|
|
4736
4796
|
subpath: parsed.subpath,
|
|
@@ -4739,13 +4799,13 @@ async function runAdd(args, options = {}) {
|
|
|
4739
4799
|
getToken: getGitHubToken,
|
|
4740
4800
|
includeInternal
|
|
4741
4801
|
});
|
|
4742
|
-
if (!blobResult) spinner$3.stop(import_picocolors.default.dim("Falling back to clone…"));
|
|
4743
4802
|
}
|
|
4744
4803
|
if (blobResult) {
|
|
4745
4804
|
skills = blobResult.skills;
|
|
4746
4805
|
spinner$3.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
|
|
4747
4806
|
} else {
|
|
4748
|
-
spinner$3.
|
|
4807
|
+
if (attemptedBlobInstall) spinner$3.message("Cloning repository…");
|
|
4808
|
+
else spinner$3.start("Cloning repository…");
|
|
4749
4809
|
tempDir = await cloneRepo(parsed.url, parsed.ref);
|
|
4750
4810
|
spinner$3.stop("Repository cloned");
|
|
4751
4811
|
spinner$3.start("Discovering skills…");
|
|
@@ -4768,7 +4828,7 @@ async function runAdd(args, options = {}) {
|
|
|
4768
4828
|
spinner$3.stop(import_picocolors.default.red("No skills found"));
|
|
4769
4829
|
outro(import_picocolors.default.red("No valid skills found. Skills require a SKILL.md with name and description."));
|
|
4770
4830
|
await cleanup(tempDir);
|
|
4771
|
-
|
|
4831
|
+
emitJsonAndExit(1, "No valid skills found. Skills require a SKILL.md with name and description.");
|
|
4772
4832
|
}
|
|
4773
4833
|
if (!blobResult) spinner$3.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
|
|
4774
4834
|
if (options.list) {
|
|
@@ -4801,7 +4861,7 @@ async function runAdd(args, options = {}) {
|
|
|
4801
4861
|
console.log();
|
|
4802
4862
|
outro("Use --skill <name> to install specific skills");
|
|
4803
4863
|
await cleanup(tempDir);
|
|
4804
|
-
|
|
4864
|
+
emitJsonAndExit(0);
|
|
4805
4865
|
}
|
|
4806
4866
|
let selectedSkills;
|
|
4807
4867
|
if (options.skill?.includes("*")) {
|
|
@@ -4809,12 +4869,19 @@ async function runAdd(args, options = {}) {
|
|
|
4809
4869
|
log.info(`Installing all ${skills.length} skills`);
|
|
4810
4870
|
} else if (options.skill && options.skill.length > 0) {
|
|
4811
4871
|
selectedSkills = filterSkills(skills, options.skill);
|
|
4872
|
+
if (jsonMode) {
|
|
4873
|
+
for (const requested of options.skill) if (filterSkills(skills, [requested]).length === 0) jsonResults.push({
|
|
4874
|
+
name: requested,
|
|
4875
|
+
status: "skipped",
|
|
4876
|
+
reason: "No matching skill found in source"
|
|
4877
|
+
});
|
|
4878
|
+
}
|
|
4812
4879
|
if (selectedSkills.length === 0) {
|
|
4813
4880
|
log.error(`No matching skills found for: ${options.skill.join(", ")}`);
|
|
4814
4881
|
log.info("Available skills:");
|
|
4815
4882
|
for (const s of skills) log.message(` - ${getSkillDisplayName(s)}`);
|
|
4816
4883
|
await cleanup(tempDir);
|
|
4817
|
-
|
|
4884
|
+
emitJsonAndExit(1, `No matching skills found for: ${options.skill.join(", ")}`);
|
|
4818
4885
|
}
|
|
4819
4886
|
log.info(`Selected ${selectedSkills.length} skill${selectedSkills.length !== 1 ? "s" : ""}: ${selectedSkills.map((s) => import_picocolors.default.cyan(getSkillDisplayName(s))).join(", ")}`);
|
|
4820
4887
|
} else if (skills.length === 1) {
|
|
@@ -4870,7 +4937,7 @@ async function runAdd(args, options = {}) {
|
|
|
4870
4937
|
log.error(`Invalid agents: ${invalidAgents.join(", ")}`);
|
|
4871
4938
|
log.info(`Valid agents: ${validAgents.join(", ")}`);
|
|
4872
4939
|
await cleanup(tempDir);
|
|
4873
|
-
|
|
4940
|
+
emitJsonAndExit(1, `Invalid agents: ${invalidAgents.join(", ")}`);
|
|
4874
4941
|
}
|
|
4875
4942
|
targetAgents = options.agent;
|
|
4876
4943
|
} else {
|
|
@@ -4889,6 +4956,7 @@ async function runAdd(args, options = {}) {
|
|
|
4889
4956
|
}
|
|
4890
4957
|
if (useEve) {
|
|
4891
4958
|
targetAgents = ["eve"];
|
|
4959
|
+
if (!options.yes) explicitlySelectedAgents.add("eve");
|
|
4892
4960
|
log.info(`Installing to: ${import_picocolors.default.cyan(EVE_AGENT_LABEL)}`);
|
|
4893
4961
|
} else {
|
|
4894
4962
|
const selected = await selectAgentsInteractive({ global: options.global });
|
|
@@ -4897,6 +4965,7 @@ async function runAdd(args, options = {}) {
|
|
|
4897
4965
|
exitInstallationCancelled();
|
|
4898
4966
|
}
|
|
4899
4967
|
targetAgents = selected;
|
|
4968
|
+
for (const agent of targetAgents) explicitlySelectedAgents.add(agent);
|
|
4900
4969
|
}
|
|
4901
4970
|
} else if (installedAgents.length === 0) if (options.yes) {
|
|
4902
4971
|
targetAgents = validAgents;
|
|
@@ -4912,6 +4981,7 @@ async function runAdd(args, options = {}) {
|
|
|
4912
4981
|
exitInstallationCancelled();
|
|
4913
4982
|
}
|
|
4914
4983
|
targetAgents = selected;
|
|
4984
|
+
for (const agent of targetAgents) explicitlySelectedAgents.add(agent);
|
|
4915
4985
|
}
|
|
4916
4986
|
else if (installedAgents.length === 1 || options.yes) {
|
|
4917
4987
|
targetAgents = ensureUniversalAgents(installedAgents);
|
|
@@ -4926,9 +4996,13 @@ async function runAdd(args, options = {}) {
|
|
|
4926
4996
|
exitInstallationCancelled();
|
|
4927
4997
|
}
|
|
4928
4998
|
targetAgents = selected;
|
|
4999
|
+
for (const agent of targetAgents) explicitlySelectedAgents.add(agent);
|
|
4929
5000
|
}
|
|
4930
5001
|
}
|
|
4931
|
-
if (options.subagent && options.subagent.length > 0
|
|
5002
|
+
if (options.subagent && options.subagent.length > 0) {
|
|
5003
|
+
explicitlySelectedAgents.add("eve");
|
|
5004
|
+
if (!targetAgents.includes("eve")) targetAgents = [...targetAgents, "eve"];
|
|
5005
|
+
}
|
|
4932
5006
|
let eveSubagentTargets = [void 0];
|
|
4933
5007
|
if (targetAgents.includes("eve")) {
|
|
4934
5008
|
const availableSubagents = getEveSubagents(process.cwd());
|
|
@@ -5052,8 +5126,10 @@ async function runAdd(args, options = {}) {
|
|
|
5052
5126
|
}
|
|
5053
5127
|
console.log();
|
|
5054
5128
|
note(summaryLines.join("\n"), "Installation Summary");
|
|
5129
|
+
let auditDataForJson = null;
|
|
5055
5130
|
try {
|
|
5056
5131
|
const auditData = await auditPromise;
|
|
5132
|
+
auditDataForJson = auditData;
|
|
5057
5133
|
if (auditData && ownerRepoForAudit) {
|
|
5058
5134
|
const securityLines = buildSecurityLines(auditData, selectedSkills.map((s) => ({
|
|
5059
5135
|
slug: getSkillDisplayName(s),
|
|
@@ -5082,12 +5158,14 @@ async function runAdd(args, options = {}) {
|
|
|
5082
5158
|
}, agent, {
|
|
5083
5159
|
global: installGlobally,
|
|
5084
5160
|
mode: installMode,
|
|
5085
|
-
eveSubagent: subagent
|
|
5161
|
+
eveSubagent: subagent,
|
|
5162
|
+
createMissingAgentRoot: explicitlySelectedAgents.has(agent)
|
|
5086
5163
|
});
|
|
5087
5164
|
} else result = await installSkillForAgent(skill, agent, {
|
|
5088
5165
|
global: installGlobally,
|
|
5089
5166
|
mode: installMode,
|
|
5090
|
-
eveSubagent: subagent
|
|
5167
|
+
eveSubagent: subagent,
|
|
5168
|
+
createMissingAgentRoot: explicitlySelectedAgents.has(agent)
|
|
5091
5169
|
});
|
|
5092
5170
|
results.push({
|
|
5093
5171
|
skill: getSkillDisplayName(skill),
|
|
@@ -5100,6 +5178,7 @@ async function runAdd(args, options = {}) {
|
|
|
5100
5178
|
console.log();
|
|
5101
5179
|
const successful = results.filter((r) => r.success);
|
|
5102
5180
|
const failed = results.filter((r) => !r.success);
|
|
5181
|
+
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
5103
5182
|
const skillFiles = {};
|
|
5104
5183
|
for (const skill of selectedSkills) if (blobResult && "repoPath" in skill) skillFiles[skill.name] = skill.repoPath;
|
|
5105
5184
|
else if (tempDir && skill.path === tempDir) skillFiles[skill.name] = "SKILL.md";
|
|
@@ -5127,8 +5206,16 @@ async function runAdd(args, options = {}) {
|
|
|
5127
5206
|
skillFiles: JSON.stringify(skillFiles),
|
|
5128
5207
|
metadata: options.metadata
|
|
5129
5208
|
});
|
|
5209
|
+
const installedSkillHashes = /* @__PURE__ */ new Map();
|
|
5210
|
+
if (successful.length > 0 && (jsonMode || !installGlobally)) for (const skill of selectedSkills) {
|
|
5211
|
+
const skillDisplayName = getSkillDisplayName(skill);
|
|
5212
|
+
if (!successfulSkillNames.has(skillDisplayName)) continue;
|
|
5213
|
+
try {
|
|
5214
|
+
const computedHash = blobResult && "snapshotHash" in skill ? skill.snapshotHash : await computeSkillFolderHash(skill.path);
|
|
5215
|
+
installedSkillHashes.set(skillDisplayName, computedHash);
|
|
5216
|
+
} catch {}
|
|
5217
|
+
}
|
|
5130
5218
|
if (successful.length > 0 && installGlobally && normalizedSource) {
|
|
5131
|
-
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
5132
5219
|
let cachedTree;
|
|
5133
5220
|
if (parsed.type === "github" && !blobResult) cachedTree = await fetchRepoTree(normalizedSource, parsed.ref, getGitHubToken);
|
|
5134
5221
|
for (const skill of selectedSkills) {
|
|
@@ -5159,13 +5246,13 @@ async function runAdd(args, options = {}) {
|
|
|
5159
5246
|
}
|
|
5160
5247
|
}
|
|
5161
5248
|
if (successful.length > 0 && !installGlobally && !directDownload) {
|
|
5162
|
-
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
5163
5249
|
const eveSubagents = targetAgents.includes("eve") ? eveSubagentTargets.map((s) => s ?? "") : void 0;
|
|
5164
5250
|
const recordSubagents = eveSubagents && (eveSubagents.length > 1 || eveSubagents.some((s) => s !== ""));
|
|
5165
5251
|
for (const skill of selectedSkills) {
|
|
5166
5252
|
const skillDisplayName = getSkillDisplayName(skill);
|
|
5167
5253
|
if (successfulSkillNames.has(skillDisplayName)) try {
|
|
5168
|
-
const computedHash =
|
|
5254
|
+
const computedHash = installedSkillHashes.get(skillDisplayName);
|
|
5255
|
+
if (computedHash === void 0) continue;
|
|
5169
5256
|
const skillPathValue = skillFiles[skill.name];
|
|
5170
5257
|
await addSkillToLocalLock(skill.name, {
|
|
5171
5258
|
source: lockSource || parsed.url,
|
|
@@ -5179,6 +5266,37 @@ async function runAdd(args, options = {}) {
|
|
|
5179
5266
|
} catch {}
|
|
5180
5267
|
}
|
|
5181
5268
|
}
|
|
5269
|
+
if (jsonMode) {
|
|
5270
|
+
const jsonSource = normalizedSource ?? (parsed.type === "local" ? parsed.localPath : parsed.url);
|
|
5271
|
+
for (const skill of selectedSkills) {
|
|
5272
|
+
const name = getSkillDisplayName(skill);
|
|
5273
|
+
const skillResults = results.filter((r) => r.skill === name);
|
|
5274
|
+
const failures = skillResults.filter((r) => !r.success);
|
|
5275
|
+
if (failures.length > 0) {
|
|
5276
|
+
jsonResults.push({
|
|
5277
|
+
name,
|
|
5278
|
+
status: "failed",
|
|
5279
|
+
error: failures[0].error ?? "Installation failed"
|
|
5280
|
+
});
|
|
5281
|
+
continue;
|
|
5282
|
+
}
|
|
5283
|
+
jsonResults.push({
|
|
5284
|
+
name,
|
|
5285
|
+
status: "installed",
|
|
5286
|
+
source: jsonSource,
|
|
5287
|
+
ref: parsed.ref ?? null,
|
|
5288
|
+
hash: installedSkillHashes.get(name) ?? null,
|
|
5289
|
+
path: skillResults[0]?.canonicalPath ?? skillResults[0]?.path,
|
|
5290
|
+
scope: installGlobally ? "global" : "project",
|
|
5291
|
+
agents: skillResults.filter((r) => !r.skipped).map((r) => r.agent),
|
|
5292
|
+
mode: skillResults[0]?.mode ?? installMode,
|
|
5293
|
+
security: buildJsonSecurity(auditDataForJson, name, ownerRepoForAudit)
|
|
5294
|
+
});
|
|
5295
|
+
}
|
|
5296
|
+
emitJson();
|
|
5297
|
+
if (failed.length > 0 || jsonResults.some((result) => result.status === "skipped")) process.exitCode = 1;
|
|
5298
|
+
return;
|
|
5299
|
+
}
|
|
5182
5300
|
if (successful.length > 0) {
|
|
5183
5301
|
const bySkill = /* @__PURE__ */ new Map();
|
|
5184
5302
|
const groupedResults = {};
|
|
@@ -5256,8 +5374,10 @@ async function runAdd(args, options = {}) {
|
|
|
5256
5374
|
} else log.error(error instanceof Error ? error.message : "Unknown error occurred");
|
|
5257
5375
|
showInstallTip();
|
|
5258
5376
|
outro(import_picocolors.default.red("Installation failed"));
|
|
5259
|
-
|
|
5377
|
+
emitJsonAndExit(1, error instanceof GitCloneError ? `Failed to clone repository\n${error.message}` : error instanceof Error ? error.message : "Unknown error occurred");
|
|
5260
5378
|
} finally {
|
|
5379
|
+
if (jsonMode) process.removeListener("exit", emitJsonOnExit);
|
|
5380
|
+
restoreStdout();
|
|
5261
5381
|
await cleanup(tempDir);
|
|
5262
5382
|
}
|
|
5263
5383
|
}
|
|
@@ -5345,6 +5465,7 @@ function parseAddOptions(args) {
|
|
|
5345
5465
|
errors.push("--metadata must be valid JSON");
|
|
5346
5466
|
}
|
|
5347
5467
|
} else if (arg === "--full-depth") options.fullDepth = true;
|
|
5468
|
+
else if (arg === "--json") options.json = true;
|
|
5348
5469
|
else if (arg === "--copy") options.copy = true;
|
|
5349
5470
|
else if (arg === "--subagent") {
|
|
5350
5471
|
options.subagent = options.subagent || [];
|
|
@@ -7179,7 +7300,8 @@ async function runUpdate(args = []) {
|
|
|
7179
7300
|
const BLOB_ALLOWED_OWNERS = [
|
|
7180
7301
|
"vercel",
|
|
7181
7302
|
"vercel-labs",
|
|
7182
|
-
"heygen-com"
|
|
7303
|
+
"heygen-com",
|
|
7304
|
+
"remotion-dev"
|
|
7183
7305
|
];
|
|
7184
7306
|
const EXCLUDE_FILES = /* @__PURE__ */ new Set(["metadata.json"]);
|
|
7185
7307
|
const EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -7661,6 +7783,7 @@ ${BOLD}Add Options:${RESET}
|
|
|
7661
7783
|
--subagent <names> Install to Eve subagents (use 'root' for the root agent)
|
|
7662
7784
|
--all Shorthand for --skill '*' --agent '*' -y
|
|
7663
7785
|
--full-depth Search all subdirectories even when a root SKILL.md exists
|
|
7786
|
+
--json Output results as JSON (machine-readable, no ANSI codes)
|
|
7664
7787
|
|
|
7665
7788
|
${BOLD}Use Options:${RESET}
|
|
7666
7789
|
-s, --skill <skill> Specify the skill to use
|
|
@@ -7694,6 +7817,7 @@ ${BOLD}Examples:${RESET}
|
|
|
7694
7817
|
${DIM}$${RESET} skills add vercel-labs/agent-skills -g
|
|
7695
7818
|
${DIM}$${RESET} skills add vercel-labs/agent-skills --agent claude-code cursor
|
|
7696
7819
|
${DIM}$${RESET} skills add vercel-labs/agent-skills --skill pr-review commit
|
|
7820
|
+
${DIM}$${RESET} skills add vercel-labs/agent-skills --json -y ${DIM}# JSON output${RESET}
|
|
7697
7821
|
${DIM}$${RESET} skills remove ${DIM}# interactive remove${RESET}
|
|
7698
7822
|
${DIM}$${RESET} skills remove web-design ${DIM}# remove by name${RESET}
|
|
7699
7823
|
${DIM}$${RESET} skills rm --global frontend-design
|
|
@@ -7828,10 +7952,11 @@ async function main() {
|
|
|
7828
7952
|
case "install":
|
|
7829
7953
|
case "a":
|
|
7830
7954
|
case "add": {
|
|
7831
|
-
if (!inAgent) showLogo();
|
|
7832
7955
|
const { source: addSource, options: addOpts, errors } = parseAddOptions(restArgs);
|
|
7956
|
+
if (!inAgent && !addOpts.json) showLogo();
|
|
7833
7957
|
if (errors.length > 0) {
|
|
7834
7958
|
for (const error of errors) console.error(`Error: ${error}`);
|
|
7959
|
+
if (addOpts.json) console.log("[]");
|
|
7835
7960
|
process.exitCode = 1;
|
|
7836
7961
|
break;
|
|
7837
7962
|
}
|