skills 1.4.2 → 1.4.4
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 +75 -649
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -21,6 +21,13 @@ import { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpat
|
|
|
21
21
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
22
22
|
function getOwnerRepo(parsed) {
|
|
23
23
|
if (parsed.type === "local") return null;
|
|
24
|
+
const sshMatch = parsed.url.match(/^git@[^:]+:(.+)$/);
|
|
25
|
+
if (sshMatch) {
|
|
26
|
+
let path = sshMatch[1];
|
|
27
|
+
path = path.replace(/\.git$/, "");
|
|
28
|
+
if (path.includes("/")) return path;
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
24
31
|
if (!parsed.url.startsWith("http://") && !parsed.url.startsWith("https://")) return null;
|
|
25
32
|
try {
|
|
26
33
|
let path = new URL(parsed.url).pathname.slice(1);
|
|
@@ -46,22 +53,22 @@ async function isRepoPrivate(owner, repo) {
|
|
|
46
53
|
return null;
|
|
47
54
|
}
|
|
48
55
|
}
|
|
56
|
+
function sanitizeSubpath(subpath) {
|
|
57
|
+
const segments = subpath.replace(/\\/g, "/").split("/");
|
|
58
|
+
for (const segment of segments) if (segment === "..") throw new Error(`Unsafe subpath: "${subpath}" contains path traversal segments. Subpaths must not contain ".." components.`);
|
|
59
|
+
return subpath;
|
|
60
|
+
}
|
|
49
61
|
function isLocalPath(input) {
|
|
50
62
|
return isAbsolute(input) || input.startsWith("./") || input.startsWith("../") || input === "." || input === ".." || /^[a-zA-Z]:[/\\]/.test(input);
|
|
51
63
|
}
|
|
52
|
-
function isDirectSkillUrl(input) {
|
|
53
|
-
if (!input.startsWith("http://") && !input.startsWith("https://")) return false;
|
|
54
|
-
if (!input.toLowerCase().endsWith("/skill.md")) return false;
|
|
55
|
-
if (input.includes("github.com/") && !input.includes("raw.githubusercontent.com")) {
|
|
56
|
-
if (!input.includes("/blob/") && !input.includes("/raw/")) return false;
|
|
57
|
-
}
|
|
58
|
-
if (input.includes("gitlab.com/") && !input.includes("/-/raw/")) return false;
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
64
|
const SOURCE_ALIASES = { "coinbase/agentWallet": "coinbase/agentic-wallet-skills" };
|
|
62
65
|
function parseSource(input) {
|
|
63
66
|
const alias = SOURCE_ALIASES[input];
|
|
64
67
|
if (alias) input = alias;
|
|
68
|
+
const githubPrefixMatch = input.match(/^github:(.+)$/);
|
|
69
|
+
if (githubPrefixMatch) return parseSource(githubPrefixMatch[1]);
|
|
70
|
+
const gitlabPrefixMatch = input.match(/^gitlab:(.+)$/);
|
|
71
|
+
if (gitlabPrefixMatch) return parseSource(`https://gitlab.com/${gitlabPrefixMatch[1]}`);
|
|
65
72
|
if (isLocalPath(input)) {
|
|
66
73
|
const resolvedPath = resolve(input);
|
|
67
74
|
return {
|
|
@@ -70,10 +77,6 @@ function parseSource(input) {
|
|
|
70
77
|
localPath: resolvedPath
|
|
71
78
|
};
|
|
72
79
|
}
|
|
73
|
-
if (isDirectSkillUrl(input)) return {
|
|
74
|
-
type: "direct-url",
|
|
75
|
-
url: input
|
|
76
|
-
};
|
|
77
80
|
const githubTreeWithPathMatch = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/);
|
|
78
81
|
if (githubTreeWithPathMatch) {
|
|
79
82
|
const [, owner, repo, ref, subpath] = githubTreeWithPathMatch;
|
|
@@ -81,7 +84,7 @@ function parseSource(input) {
|
|
|
81
84
|
type: "github",
|
|
82
85
|
url: `https://github.com/${owner}/${repo}.git`,
|
|
83
86
|
ref,
|
|
84
|
-
subpath
|
|
87
|
+
subpath: subpath ? sanitizeSubpath(subpath) : subpath
|
|
85
88
|
};
|
|
86
89
|
}
|
|
87
90
|
const githubTreeMatch = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)$/);
|
|
@@ -108,7 +111,7 @@ function parseSource(input) {
|
|
|
108
111
|
type: "gitlab",
|
|
109
112
|
url: `${protocol}://${hostname}/${repoPath.replace(/\.git$/, "")}.git`,
|
|
110
113
|
ref,
|
|
111
|
-
subpath
|
|
114
|
+
subpath: subpath ? sanitizeSubpath(subpath) : subpath
|
|
112
115
|
};
|
|
113
116
|
}
|
|
114
117
|
const gitlabTreeMatch = input.match(/^(https?):\/\/([^/]+)\/(.+?)\/-\/tree\/([^/]+)$/);
|
|
@@ -143,7 +146,7 @@ function parseSource(input) {
|
|
|
143
146
|
return {
|
|
144
147
|
type: "github",
|
|
145
148
|
url: `https://github.com/${owner}/${repo}.git`,
|
|
146
|
-
subpath
|
|
149
|
+
subpath: subpath ? sanitizeSubpath(subpath) : subpath
|
|
147
150
|
};
|
|
148
151
|
}
|
|
149
152
|
if (isWellKnownUrl(input)) return {
|
|
@@ -162,10 +165,8 @@ function isWellKnownUrl(input) {
|
|
|
162
165
|
if ([
|
|
163
166
|
"github.com",
|
|
164
167
|
"gitlab.com",
|
|
165
|
-
"huggingface.co",
|
|
166
168
|
"raw.githubusercontent.com"
|
|
167
169
|
].includes(parsed.hostname)) return false;
|
|
168
|
-
if (input.toLowerCase().endsWith("/skill.md")) return false;
|
|
169
170
|
if (input.endsWith(".git")) return false;
|
|
170
171
|
return true;
|
|
171
172
|
} catch {
|
|
@@ -501,9 +502,15 @@ async function findSkillDirs(dir, depth = 0, maxDepth = 5) {
|
|
|
501
502
|
return [];
|
|
502
503
|
}
|
|
503
504
|
}
|
|
505
|
+
function isSubpathSafe(basePath, subpath) {
|
|
506
|
+
const normalizedBase = normalize(resolve(basePath));
|
|
507
|
+
const normalizedTarget = normalize(resolve(join(basePath, subpath)));
|
|
508
|
+
return normalizedTarget.startsWith(normalizedBase + sep) || normalizedTarget === normalizedBase;
|
|
509
|
+
}
|
|
504
510
|
async function discoverSkills(basePath, subpath, options) {
|
|
505
511
|
const skills = [];
|
|
506
512
|
const seenNames = /* @__PURE__ */ new Set();
|
|
513
|
+
if (subpath && !isSubpathSafe(basePath, subpath)) throw new Error(`Invalid subpath: "${subpath}" resolves outside the repository directory. Subpath must not contain ".." segments that escape the base path.`);
|
|
507
514
|
const searchPath = subpath ? join(basePath, subpath) : basePath;
|
|
508
515
|
const pluginGroupings = await getPluginGroupings(searchPath);
|
|
509
516
|
const enhanceSkill = (skill) => {
|
|
@@ -1179,78 +1186,6 @@ function getCanonicalPath(skillName, options = {}) {
|
|
|
1179
1186
|
if (!isPathSafe(canonicalBase, canonicalPath)) throw new Error("Invalid skill name: potential path traversal detected");
|
|
1180
1187
|
return canonicalPath;
|
|
1181
1188
|
}
|
|
1182
|
-
async function installRemoteSkillForAgent(skill, agentType, options = {}) {
|
|
1183
|
-
const agent = agents[agentType];
|
|
1184
|
-
const isGlobal = options.global ?? false;
|
|
1185
|
-
const cwd = options.cwd || process.cwd();
|
|
1186
|
-
const installMode = options.mode ?? "symlink";
|
|
1187
|
-
if (isGlobal && agent.globalSkillsDir === void 0) return {
|
|
1188
|
-
success: false,
|
|
1189
|
-
path: "",
|
|
1190
|
-
mode: installMode,
|
|
1191
|
-
error: `${agent.displayName} does not support global skill installation`
|
|
1192
|
-
};
|
|
1193
|
-
const skillName = sanitizeName(skill.installName);
|
|
1194
|
-
const canonicalBase = getCanonicalSkillsDir(isGlobal, cwd);
|
|
1195
|
-
const canonicalDir = join(canonicalBase, skillName);
|
|
1196
|
-
const agentBase = getAgentBaseDir(agentType, isGlobal, cwd);
|
|
1197
|
-
const agentDir = join(agentBase, skillName);
|
|
1198
|
-
if (!isPathSafe(canonicalBase, canonicalDir)) return {
|
|
1199
|
-
success: false,
|
|
1200
|
-
path: agentDir,
|
|
1201
|
-
mode: installMode,
|
|
1202
|
-
error: "Invalid skill name: potential path traversal detected"
|
|
1203
|
-
};
|
|
1204
|
-
if (!isPathSafe(agentBase, agentDir)) return {
|
|
1205
|
-
success: false,
|
|
1206
|
-
path: agentDir,
|
|
1207
|
-
mode: installMode,
|
|
1208
|
-
error: "Invalid skill name: potential path traversal detected"
|
|
1209
|
-
};
|
|
1210
|
-
try {
|
|
1211
|
-
if (installMode === "copy") {
|
|
1212
|
-
await cleanAndCreateDirectory(agentDir);
|
|
1213
|
-
await writeFile(join(agentDir, "SKILL.md"), skill.content, "utf-8");
|
|
1214
|
-
return {
|
|
1215
|
-
success: true,
|
|
1216
|
-
path: agentDir,
|
|
1217
|
-
mode: "copy"
|
|
1218
|
-
};
|
|
1219
|
-
}
|
|
1220
|
-
await cleanAndCreateDirectory(canonicalDir);
|
|
1221
|
-
await writeFile(join(canonicalDir, "SKILL.md"), skill.content, "utf-8");
|
|
1222
|
-
if (isGlobal && isUniversalAgent(agentType)) return {
|
|
1223
|
-
success: true,
|
|
1224
|
-
path: canonicalDir,
|
|
1225
|
-
canonicalPath: canonicalDir,
|
|
1226
|
-
mode: "symlink"
|
|
1227
|
-
};
|
|
1228
|
-
if (!await createSymlink(canonicalDir, agentDir)) {
|
|
1229
|
-
await cleanAndCreateDirectory(agentDir);
|
|
1230
|
-
await writeFile(join(agentDir, "SKILL.md"), skill.content, "utf-8");
|
|
1231
|
-
return {
|
|
1232
|
-
success: true,
|
|
1233
|
-
path: agentDir,
|
|
1234
|
-
canonicalPath: canonicalDir,
|
|
1235
|
-
mode: "symlink",
|
|
1236
|
-
symlinkFailed: true
|
|
1237
|
-
};
|
|
1238
|
-
}
|
|
1239
|
-
return {
|
|
1240
|
-
success: true,
|
|
1241
|
-
path: agentDir,
|
|
1242
|
-
canonicalPath: canonicalDir,
|
|
1243
|
-
mode: "symlink"
|
|
1244
|
-
};
|
|
1245
|
-
} catch (error) {
|
|
1246
|
-
return {
|
|
1247
|
-
success: false,
|
|
1248
|
-
path: agentDir,
|
|
1249
|
-
mode: installMode,
|
|
1250
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1251
|
-
};
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
1189
|
async function installWellKnownSkillForAgent(skill, agentType, options = {}) {
|
|
1255
1190
|
const agent = agents[agentType];
|
|
1256
1191
|
const isGlobal = options.global ?? false;
|
|
@@ -1495,108 +1430,7 @@ var ProviderRegistryImpl = class {
|
|
|
1495
1430
|
return [...this.providers];
|
|
1496
1431
|
}
|
|
1497
1432
|
};
|
|
1498
|
-
|
|
1499
|
-
function registerProvider(provider) {
|
|
1500
|
-
registry.register(provider);
|
|
1501
|
-
}
|
|
1502
|
-
function findProvider(url) {
|
|
1503
|
-
return registry.findProvider(url);
|
|
1504
|
-
}
|
|
1505
|
-
var MintlifyProvider = class {
|
|
1506
|
-
id = "mintlify";
|
|
1507
|
-
displayName = "Mintlify";
|
|
1508
|
-
match(url) {
|
|
1509
|
-
if (!url.startsWith("http://") && !url.startsWith("https://")) return { matches: false };
|
|
1510
|
-
if (!url.toLowerCase().endsWith("/skill.md")) return { matches: false };
|
|
1511
|
-
if (url.includes("github.com") || url.includes("gitlab.com")) return { matches: false };
|
|
1512
|
-
if (url.includes("huggingface.co")) return { matches: false };
|
|
1513
|
-
return { matches: true };
|
|
1514
|
-
}
|
|
1515
|
-
async fetchSkill(url) {
|
|
1516
|
-
try {
|
|
1517
|
-
const response = await fetch(url, { signal: AbortSignal.timeout(3e4) });
|
|
1518
|
-
if (!response.ok) return null;
|
|
1519
|
-
const content = await response.text();
|
|
1520
|
-
const { data } = (0, import_gray_matter.default)(content);
|
|
1521
|
-
const mintlifySite = data.metadata?.["mintlify-proj"];
|
|
1522
|
-
if (!mintlifySite) return null;
|
|
1523
|
-
if (!data.name || !data.description) return null;
|
|
1524
|
-
return {
|
|
1525
|
-
name: data.name,
|
|
1526
|
-
description: data.description,
|
|
1527
|
-
content,
|
|
1528
|
-
installName: mintlifySite,
|
|
1529
|
-
sourceUrl: url,
|
|
1530
|
-
metadata: data.metadata
|
|
1531
|
-
};
|
|
1532
|
-
} catch {
|
|
1533
|
-
return null;
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
toRawUrl(url) {
|
|
1537
|
-
return url;
|
|
1538
|
-
}
|
|
1539
|
-
getSourceIdentifier(url) {
|
|
1540
|
-
return "mintlify/com";
|
|
1541
|
-
}
|
|
1542
|
-
};
|
|
1543
|
-
const mintlifyProvider = new MintlifyProvider();
|
|
1544
|
-
var HuggingFaceProvider = class {
|
|
1545
|
-
id = "huggingface";
|
|
1546
|
-
displayName = "HuggingFace";
|
|
1547
|
-
HOST = "huggingface.co";
|
|
1548
|
-
match(url) {
|
|
1549
|
-
if (!url.startsWith("http://") && !url.startsWith("https://")) return { matches: false };
|
|
1550
|
-
try {
|
|
1551
|
-
if (new URL(url).hostname !== this.HOST) return { matches: false };
|
|
1552
|
-
} catch {
|
|
1553
|
-
return { matches: false };
|
|
1554
|
-
}
|
|
1555
|
-
if (!url.toLowerCase().endsWith("/skill.md")) return { matches: false };
|
|
1556
|
-
if (!url.includes("/spaces/")) return { matches: false };
|
|
1557
|
-
return { matches: true };
|
|
1558
|
-
}
|
|
1559
|
-
async fetchSkill(url) {
|
|
1560
|
-
try {
|
|
1561
|
-
const rawUrl = this.toRawUrl(url);
|
|
1562
|
-
const response = await fetch(rawUrl, { signal: AbortSignal.timeout(3e4) });
|
|
1563
|
-
if (!response.ok) return null;
|
|
1564
|
-
const content = await response.text();
|
|
1565
|
-
const { data } = (0, import_gray_matter.default)(content);
|
|
1566
|
-
if (!data.name || !data.description) return null;
|
|
1567
|
-
const parsed = this.parseUrl(url);
|
|
1568
|
-
if (!parsed) return null;
|
|
1569
|
-
const installName = data.metadata?.["install-name"] || parsed.repo;
|
|
1570
|
-
return {
|
|
1571
|
-
name: data.name,
|
|
1572
|
-
description: data.description,
|
|
1573
|
-
content,
|
|
1574
|
-
installName,
|
|
1575
|
-
sourceUrl: url,
|
|
1576
|
-
metadata: data.metadata
|
|
1577
|
-
};
|
|
1578
|
-
} catch {
|
|
1579
|
-
return null;
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
toRawUrl(url) {
|
|
1583
|
-
return url.replace("/blob/", "/raw/");
|
|
1584
|
-
}
|
|
1585
|
-
getSourceIdentifier(url) {
|
|
1586
|
-
const parsed = this.parseUrl(url);
|
|
1587
|
-
if (!parsed) return "huggingface/unknown";
|
|
1588
|
-
return `huggingface/${parsed.owner}/${parsed.repo}`;
|
|
1589
|
-
}
|
|
1590
|
-
parseUrl(url) {
|
|
1591
|
-
const match = url.match(/\/spaces\/([^/]+)\/([^/]+)/);
|
|
1592
|
-
if (!match || !match[1] || !match[2]) return null;
|
|
1593
|
-
return {
|
|
1594
|
-
owner: match[1],
|
|
1595
|
-
repo: match[2]
|
|
1596
|
-
};
|
|
1597
|
-
}
|
|
1598
|
-
};
|
|
1599
|
-
const huggingFaceProvider = new HuggingFaceProvider();
|
|
1433
|
+
new ProviderRegistryImpl();
|
|
1600
1434
|
var WellKnownProvider = class {
|
|
1601
1435
|
id = "well-known";
|
|
1602
1436
|
displayName = "Well-Known Skills";
|
|
@@ -1753,15 +1587,9 @@ var WellKnownProvider = class {
|
|
|
1753
1587
|
}
|
|
1754
1588
|
getSourceIdentifier(url) {
|
|
1755
1589
|
try {
|
|
1756
|
-
|
|
1757
|
-
const hostParts = parsed.hostname.split(".");
|
|
1758
|
-
if (hostParts.length >= 2) {
|
|
1759
|
-
const tld = hostParts[hostParts.length - 1];
|
|
1760
|
-
return `${hostParts[hostParts.length - 2]}/${tld}`;
|
|
1761
|
-
}
|
|
1762
|
-
return parsed.hostname.replace(".", "/");
|
|
1590
|
+
return new URL(url).hostname.replace(/^www\./, "");
|
|
1763
1591
|
} catch {
|
|
1764
|
-
return "unknown
|
|
1592
|
+
return "unknown";
|
|
1765
1593
|
}
|
|
1766
1594
|
}
|
|
1767
1595
|
async hasSkillsIndex(url) {
|
|
@@ -1769,28 +1597,6 @@ var WellKnownProvider = class {
|
|
|
1769
1597
|
}
|
|
1770
1598
|
};
|
|
1771
1599
|
const wellKnownProvider = new WellKnownProvider();
|
|
1772
|
-
registerProvider(mintlifyProvider);
|
|
1773
|
-
registerProvider(huggingFaceProvider);
|
|
1774
|
-
async function fetchMintlifySkill(url) {
|
|
1775
|
-
try {
|
|
1776
|
-
const response = await fetch(url, { signal: AbortSignal.timeout(3e4) });
|
|
1777
|
-
if (!response.ok) return null;
|
|
1778
|
-
const content = await response.text();
|
|
1779
|
-
const { data } = (0, import_gray_matter.default)(content);
|
|
1780
|
-
const mintlifySite = data.metadata?.["mintlify-proj"];
|
|
1781
|
-
if (!mintlifySite) return null;
|
|
1782
|
-
if (!data.name || !data.description) return null;
|
|
1783
|
-
return {
|
|
1784
|
-
name: data.name,
|
|
1785
|
-
description: data.description,
|
|
1786
|
-
content,
|
|
1787
|
-
mintlifySite,
|
|
1788
|
-
sourceUrl: url
|
|
1789
|
-
};
|
|
1790
|
-
} catch {
|
|
1791
|
-
return null;
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
1600
|
const AGENTS_DIR$1 = ".agents";
|
|
1795
1601
|
const LOCK_FILE$1 = ".skill-lock.json";
|
|
1796
1602
|
const CURRENT_VERSION$1 = 3;
|
|
@@ -1967,7 +1773,7 @@ function createEmptyLocalLock() {
|
|
|
1967
1773
|
skills: {}
|
|
1968
1774
|
};
|
|
1969
1775
|
}
|
|
1970
|
-
var version$1 = "1.4.
|
|
1776
|
+
var version$1 = "1.4.4";
|
|
1971
1777
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
1972
1778
|
async function isSourcePrivate(source) {
|
|
1973
1779
|
const ownerRepo = parseOwnerRepo(source);
|
|
@@ -2133,232 +1939,6 @@ async function selectAgentsInteractive(options) {
|
|
|
2133
1939
|
return selected;
|
|
2134
1940
|
}
|
|
2135
1941
|
setVersion(version$1);
|
|
2136
|
-
async function handleRemoteSkill(source, url, options, spinner) {
|
|
2137
|
-
const provider = findProvider(url);
|
|
2138
|
-
if (!provider) {
|
|
2139
|
-
await handleDirectUrlSkillLegacy(source, url, options, spinner);
|
|
2140
|
-
return;
|
|
2141
|
-
}
|
|
2142
|
-
spinner.start(`Fetching skill.md from ${provider.displayName}...`);
|
|
2143
|
-
const providerSkill = await provider.fetchSkill(url);
|
|
2144
|
-
if (!providerSkill) {
|
|
2145
|
-
spinner.stop(import_picocolors.default.red("Invalid skill"));
|
|
2146
|
-
Se(import_picocolors.default.red("Could not fetch skill.md or missing required frontmatter (name, description)."));
|
|
2147
|
-
process.exit(1);
|
|
2148
|
-
}
|
|
2149
|
-
const remoteSkill = {
|
|
2150
|
-
name: providerSkill.name,
|
|
2151
|
-
description: providerSkill.description,
|
|
2152
|
-
content: providerSkill.content,
|
|
2153
|
-
installName: providerSkill.installName,
|
|
2154
|
-
sourceUrl: providerSkill.sourceUrl,
|
|
2155
|
-
providerId: provider.id,
|
|
2156
|
-
sourceIdentifier: provider.getSourceIdentifier(url),
|
|
2157
|
-
metadata: providerSkill.metadata
|
|
2158
|
-
};
|
|
2159
|
-
spinner.stop(`Found skill: ${import_picocolors.default.cyan(remoteSkill.installName)}`);
|
|
2160
|
-
M.info(`Skill: ${import_picocolors.default.cyan(remoteSkill.name)}`);
|
|
2161
|
-
M.message(import_picocolors.default.dim(remoteSkill.description));
|
|
2162
|
-
M.message(import_picocolors.default.dim(`Source: ${remoteSkill.sourceIdentifier}`));
|
|
2163
|
-
if (options.list) {
|
|
2164
|
-
console.log();
|
|
2165
|
-
M.step(import_picocolors.default.bold("Skill Details"));
|
|
2166
|
-
M.message(` ${import_picocolors.default.cyan("Name:")} ${remoteSkill.name}`);
|
|
2167
|
-
M.message(` ${import_picocolors.default.cyan("Install as:")} ${remoteSkill.installName}`);
|
|
2168
|
-
M.message(` ${import_picocolors.default.cyan("Provider:")} ${provider.displayName}`);
|
|
2169
|
-
M.message(` ${import_picocolors.default.cyan("Description:")} ${remoteSkill.description}`);
|
|
2170
|
-
console.log();
|
|
2171
|
-
Se("Run without --list to install");
|
|
2172
|
-
process.exit(0);
|
|
2173
|
-
}
|
|
2174
|
-
let targetAgents;
|
|
2175
|
-
const validAgents = Object.keys(agents);
|
|
2176
|
-
const universalAgents = getUniversalAgents();
|
|
2177
|
-
if (options.agent?.includes("*")) {
|
|
2178
|
-
targetAgents = validAgents;
|
|
2179
|
-
M.info(`Installing to all ${targetAgents.length} agents`);
|
|
2180
|
-
} else if (options.agent && options.agent.length > 0) {
|
|
2181
|
-
const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
|
|
2182
|
-
if (invalidAgents.length > 0) {
|
|
2183
|
-
M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
|
|
2184
|
-
M.info(`Valid agents: ${validAgents.join(", ")}`);
|
|
2185
|
-
process.exit(1);
|
|
2186
|
-
}
|
|
2187
|
-
targetAgents = options.agent;
|
|
2188
|
-
} else {
|
|
2189
|
-
spinner.start("Loading agents...");
|
|
2190
|
-
const installedAgents = await detectInstalledAgents();
|
|
2191
|
-
const totalAgents = Object.keys(agents).length;
|
|
2192
|
-
spinner.stop(`${totalAgents} agents`);
|
|
2193
|
-
if (installedAgents.length === 0) if (options.yes) {
|
|
2194
|
-
targetAgents = universalAgents;
|
|
2195
|
-
M.info(`Installing to universal agents`);
|
|
2196
|
-
} else {
|
|
2197
|
-
const selected = await selectAgentsInteractive({ global: options.global });
|
|
2198
|
-
if (pD(selected)) {
|
|
2199
|
-
xe("Installation cancelled");
|
|
2200
|
-
process.exit(0);
|
|
2201
|
-
}
|
|
2202
|
-
targetAgents = selected;
|
|
2203
|
-
}
|
|
2204
|
-
else if (installedAgents.length === 1 || options.yes) {
|
|
2205
|
-
targetAgents = ensureUniversalAgents(installedAgents);
|
|
2206
|
-
const { universal, symlinked } = splitAgentsByType(targetAgents);
|
|
2207
|
-
if (symlinked.length > 0) M.info(`Installing to: ${import_picocolors.default.green("universal")} + ${symlinked.map((a) => import_picocolors.default.cyan(a)).join(", ")}`);
|
|
2208
|
-
else M.info(`Installing to: ${import_picocolors.default.green("universal agents")}`);
|
|
2209
|
-
} else {
|
|
2210
|
-
const selected = await selectAgentsInteractive({ global: options.global });
|
|
2211
|
-
if (pD(selected)) {
|
|
2212
|
-
xe("Installation cancelled");
|
|
2213
|
-
process.exit(0);
|
|
2214
|
-
}
|
|
2215
|
-
targetAgents = selected;
|
|
2216
|
-
}
|
|
2217
|
-
}
|
|
2218
|
-
let installGlobally = options.global ?? false;
|
|
2219
|
-
const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
|
|
2220
|
-
if (options.global === void 0 && !options.yes && supportsGlobal) {
|
|
2221
|
-
const scope = await ve({
|
|
2222
|
-
message: "Installation scope",
|
|
2223
|
-
options: [{
|
|
2224
|
-
value: false,
|
|
2225
|
-
label: "Project",
|
|
2226
|
-
hint: "Install in current directory (committed with your project)"
|
|
2227
|
-
}, {
|
|
2228
|
-
value: true,
|
|
2229
|
-
label: "Global",
|
|
2230
|
-
hint: "Install in home directory (available across all projects)"
|
|
2231
|
-
}]
|
|
2232
|
-
});
|
|
2233
|
-
if (pD(scope)) {
|
|
2234
|
-
xe("Installation cancelled");
|
|
2235
|
-
process.exit(0);
|
|
2236
|
-
}
|
|
2237
|
-
installGlobally = scope;
|
|
2238
|
-
}
|
|
2239
|
-
let installMode = options.copy ? "copy" : "symlink";
|
|
2240
|
-
if (!options.copy && !options.yes) {
|
|
2241
|
-
const modeChoice = await ve({
|
|
2242
|
-
message: "Installation method",
|
|
2243
|
-
options: [{
|
|
2244
|
-
value: "symlink",
|
|
2245
|
-
label: "Symlink (Recommended)",
|
|
2246
|
-
hint: "Single source of truth, easy updates"
|
|
2247
|
-
}, {
|
|
2248
|
-
value: "copy",
|
|
2249
|
-
label: "Copy to all agents",
|
|
2250
|
-
hint: "Independent copies for each agent"
|
|
2251
|
-
}]
|
|
2252
|
-
});
|
|
2253
|
-
if (pD(modeChoice)) {
|
|
2254
|
-
xe("Installation cancelled");
|
|
2255
|
-
process.exit(0);
|
|
2256
|
-
}
|
|
2257
|
-
installMode = modeChoice;
|
|
2258
|
-
}
|
|
2259
|
-
const cwd = process.cwd();
|
|
2260
|
-
const overwriteChecks = await Promise.all(targetAgents.map(async (agent) => ({
|
|
2261
|
-
agent,
|
|
2262
|
-
installed: await isSkillInstalled(remoteSkill.installName, agent, { global: installGlobally })
|
|
2263
|
-
})));
|
|
2264
|
-
const overwriteStatus = new Map(overwriteChecks.map(({ agent, installed }) => [agent, installed]));
|
|
2265
|
-
const summaryLines = [];
|
|
2266
|
-
const shortCanonical = shortenPath$2(getCanonicalPath(remoteSkill.installName, { global: installGlobally }), cwd);
|
|
2267
|
-
summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
|
|
2268
|
-
summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
|
|
2269
|
-
const overwriteAgents = targetAgents.filter((a) => overwriteStatus.get(a)).map((a) => agents[a].displayName);
|
|
2270
|
-
if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
|
|
2271
|
-
console.log();
|
|
2272
|
-
Me(summaryLines.join("\n"), "Installation Summary");
|
|
2273
|
-
if (!options.yes) {
|
|
2274
|
-
const confirmed = await ye({ message: "Proceed with installation?" });
|
|
2275
|
-
if (pD(confirmed) || !confirmed) {
|
|
2276
|
-
xe("Installation cancelled");
|
|
2277
|
-
process.exit(0);
|
|
2278
|
-
}
|
|
2279
|
-
}
|
|
2280
|
-
spinner.start("Installing skill...");
|
|
2281
|
-
const results = [];
|
|
2282
|
-
for (const agent of targetAgents) {
|
|
2283
|
-
const result = await installRemoteSkillForAgent(remoteSkill, agent, {
|
|
2284
|
-
global: installGlobally,
|
|
2285
|
-
mode: installMode
|
|
2286
|
-
});
|
|
2287
|
-
results.push({
|
|
2288
|
-
skill: remoteSkill.installName,
|
|
2289
|
-
agent: agents[agent].displayName,
|
|
2290
|
-
...result
|
|
2291
|
-
});
|
|
2292
|
-
}
|
|
2293
|
-
spinner.stop("Installation complete");
|
|
2294
|
-
console.log();
|
|
2295
|
-
const successful = results.filter((r) => r.success);
|
|
2296
|
-
const failed = results.filter((r) => !r.success);
|
|
2297
|
-
if (await isSourcePrivate(remoteSkill.sourceIdentifier) !== true) track({
|
|
2298
|
-
event: "install",
|
|
2299
|
-
source: remoteSkill.sourceIdentifier,
|
|
2300
|
-
skills: remoteSkill.installName,
|
|
2301
|
-
agents: targetAgents.join(","),
|
|
2302
|
-
...installGlobally && { global: "1" },
|
|
2303
|
-
skillFiles: JSON.stringify({ [remoteSkill.installName]: url }),
|
|
2304
|
-
sourceType: remoteSkill.providerId
|
|
2305
|
-
});
|
|
2306
|
-
if (successful.length > 0 && installGlobally) try {
|
|
2307
|
-
let skillFolderHash = "";
|
|
2308
|
-
if (remoteSkill.providerId === "github") {
|
|
2309
|
-
const hash = await fetchSkillFolderHash(remoteSkill.sourceIdentifier, url);
|
|
2310
|
-
if (hash) skillFolderHash = hash;
|
|
2311
|
-
}
|
|
2312
|
-
await addSkillToLock(remoteSkill.installName, {
|
|
2313
|
-
source: remoteSkill.sourceIdentifier,
|
|
2314
|
-
sourceType: remoteSkill.providerId,
|
|
2315
|
-
sourceUrl: url,
|
|
2316
|
-
skillFolderHash
|
|
2317
|
-
});
|
|
2318
|
-
} catch {}
|
|
2319
|
-
if (successful.length > 0 && !installGlobally) try {
|
|
2320
|
-
const firstResult = successful[0];
|
|
2321
|
-
const computedHash = await computeSkillFolderHash(firstResult.canonicalPath || firstResult.path);
|
|
2322
|
-
await addSkillToLocalLock(remoteSkill.installName, {
|
|
2323
|
-
source: remoteSkill.sourceIdentifier,
|
|
2324
|
-
sourceType: remoteSkill.providerId,
|
|
2325
|
-
computedHash
|
|
2326
|
-
}, cwd);
|
|
2327
|
-
} catch {}
|
|
2328
|
-
if (successful.length > 0) {
|
|
2329
|
-
const resultLines = [];
|
|
2330
|
-
const firstResult = successful[0];
|
|
2331
|
-
if (firstResult.mode === "copy") {
|
|
2332
|
-
resultLines.push(`${import_picocolors.default.green("✓")} ${remoteSkill.installName} ${import_picocolors.default.dim("(copied)")}`);
|
|
2333
|
-
for (const r of successful) {
|
|
2334
|
-
const shortPath = shortenPath$2(r.path, cwd);
|
|
2335
|
-
resultLines.push(` ${import_picocolors.default.dim("→")} ${shortPath}`);
|
|
2336
|
-
}
|
|
2337
|
-
} else {
|
|
2338
|
-
if (firstResult.canonicalPath) {
|
|
2339
|
-
const shortPath = shortenPath$2(firstResult.canonicalPath, cwd);
|
|
2340
|
-
resultLines.push(`${import_picocolors.default.green("✓")} ${shortPath}`);
|
|
2341
|
-
} else resultLines.push(`${import_picocolors.default.green("✓")} ${remoteSkill.installName}`);
|
|
2342
|
-
resultLines.push(...buildResultLines(successful, targetAgents));
|
|
2343
|
-
}
|
|
2344
|
-
const title = import_picocolors.default.green("Installed 1 skill");
|
|
2345
|
-
Me(resultLines.join("\n"), title);
|
|
2346
|
-
const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
|
|
2347
|
-
if (symlinkFailures.length > 0) {
|
|
2348
|
-
const copiedAgentNames = symlinkFailures.map((r) => r.agent);
|
|
2349
|
-
M.warn(import_picocolors.default.yellow(`Symlinks failed for: ${formatList$1(copiedAgentNames)}`));
|
|
2350
|
-
M.message(import_picocolors.default.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
|
|
2351
|
-
}
|
|
2352
|
-
}
|
|
2353
|
-
if (failed.length > 0) {
|
|
2354
|
-
console.log();
|
|
2355
|
-
M.error(import_picocolors.default.red(`Failed to install ${failed.length}`));
|
|
2356
|
-
for (const r of failed) M.message(` ${import_picocolors.default.red("✗")} ${r.skill} → ${r.agent}: ${import_picocolors.default.dim(r.error)}`);
|
|
2357
|
-
}
|
|
2358
|
-
console.log();
|
|
2359
|
-
Se(import_picocolors.default.green("Done!") + import_picocolors.default.dim(" Review skills before use; they run with full agent permissions."));
|
|
2360
|
-
await promptForFindSkills(options, targetAgents);
|
|
2361
|
-
}
|
|
2362
1942
|
async function handleWellKnownSkills(source, url, options, spinner) {
|
|
2363
1943
|
spinner.start("Discovering skills from well-known endpoint...");
|
|
2364
1944
|
const skills = await wellKnownProvider.fetchAllSkills(url);
|
|
@@ -2639,188 +2219,6 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2639
2219
|
Se(import_picocolors.default.green("Done!") + import_picocolors.default.dim(" Review skills before use; they run with full agent permissions."));
|
|
2640
2220
|
await promptForFindSkills(options, targetAgents);
|
|
2641
2221
|
}
|
|
2642
|
-
async function handleDirectUrlSkillLegacy(source, url, options, spinner) {
|
|
2643
|
-
spinner.start("Fetching skill.md...");
|
|
2644
|
-
const mintlifySkill = await fetchMintlifySkill(url);
|
|
2645
|
-
if (!mintlifySkill) {
|
|
2646
|
-
spinner.stop(import_picocolors.default.red("Invalid skill"));
|
|
2647
|
-
Se(import_picocolors.default.red("Could not fetch skill.md or missing required frontmatter (name, description, mintlify-proj)."));
|
|
2648
|
-
process.exit(1);
|
|
2649
|
-
}
|
|
2650
|
-
const remoteSkill = {
|
|
2651
|
-
name: mintlifySkill.name,
|
|
2652
|
-
description: mintlifySkill.description,
|
|
2653
|
-
content: mintlifySkill.content,
|
|
2654
|
-
installName: mintlifySkill.mintlifySite,
|
|
2655
|
-
sourceUrl: mintlifySkill.sourceUrl,
|
|
2656
|
-
providerId: "mintlify",
|
|
2657
|
-
sourceIdentifier: "mintlify/com"
|
|
2658
|
-
};
|
|
2659
|
-
spinner.stop(`Found skill: ${import_picocolors.default.cyan(remoteSkill.installName)}`);
|
|
2660
|
-
M.info(`Skill: ${import_picocolors.default.cyan(remoteSkill.name)}`);
|
|
2661
|
-
M.message(import_picocolors.default.dim(remoteSkill.description));
|
|
2662
|
-
if (options.list) {
|
|
2663
|
-
console.log();
|
|
2664
|
-
M.step(import_picocolors.default.bold("Skill Details"));
|
|
2665
|
-
M.message(` ${import_picocolors.default.cyan("Name:")} ${remoteSkill.name}`);
|
|
2666
|
-
M.message(` ${import_picocolors.default.cyan("Site:")} ${remoteSkill.installName}`);
|
|
2667
|
-
M.message(` ${import_picocolors.default.cyan("Description:")} ${remoteSkill.description}`);
|
|
2668
|
-
console.log();
|
|
2669
|
-
Se("Run without --list to install");
|
|
2670
|
-
process.exit(0);
|
|
2671
|
-
}
|
|
2672
|
-
let targetAgents;
|
|
2673
|
-
const validAgents = Object.keys(agents);
|
|
2674
|
-
if (options.agent?.includes("*")) {
|
|
2675
|
-
targetAgents = validAgents;
|
|
2676
|
-
M.info(`Installing to all ${targetAgents.length} agents`);
|
|
2677
|
-
} else if (options.agent && options.agent.length > 0) {
|
|
2678
|
-
const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
|
|
2679
|
-
if (invalidAgents.length > 0) {
|
|
2680
|
-
M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
|
|
2681
|
-
M.info(`Valid agents: ${validAgents.join(", ")}`);
|
|
2682
|
-
process.exit(1);
|
|
2683
|
-
}
|
|
2684
|
-
targetAgents = options.agent;
|
|
2685
|
-
} else {
|
|
2686
|
-
spinner.start("Loading agents...");
|
|
2687
|
-
const installedAgents = await detectInstalledAgents();
|
|
2688
|
-
const totalAgents = Object.keys(agents).length;
|
|
2689
|
-
spinner.stop(`${totalAgents} agents`);
|
|
2690
|
-
if (installedAgents.length === 0) if (options.yes) {
|
|
2691
|
-
targetAgents = validAgents;
|
|
2692
|
-
M.info("Installing to all agents");
|
|
2693
|
-
} else {
|
|
2694
|
-
M.info("Select agents to install skills to");
|
|
2695
|
-
const selected = await promptForAgents("Which agents do you want to install to?", Object.entries(agents).map(([key, config]) => ({
|
|
2696
|
-
value: key,
|
|
2697
|
-
label: config.displayName
|
|
2698
|
-
})));
|
|
2699
|
-
if (pD(selected)) {
|
|
2700
|
-
xe("Installation cancelled");
|
|
2701
|
-
process.exit(0);
|
|
2702
|
-
}
|
|
2703
|
-
targetAgents = selected;
|
|
2704
|
-
}
|
|
2705
|
-
else if (installedAgents.length === 1 || options.yes) {
|
|
2706
|
-
targetAgents = ensureUniversalAgents(installedAgents);
|
|
2707
|
-
if (installedAgents.length === 1) {
|
|
2708
|
-
const firstAgent = installedAgents[0];
|
|
2709
|
-
M.info(`Installing to: ${import_picocolors.default.cyan(agents[firstAgent].displayName)}`);
|
|
2710
|
-
} else M.info(`Installing to: ${installedAgents.map((a) => import_picocolors.default.cyan(agents[a].displayName)).join(", ")}`);
|
|
2711
|
-
} else {
|
|
2712
|
-
const selected = await selectAgentsInteractive({ global: options.global });
|
|
2713
|
-
if (pD(selected)) {
|
|
2714
|
-
xe("Installation cancelled");
|
|
2715
|
-
process.exit(0);
|
|
2716
|
-
}
|
|
2717
|
-
targetAgents = selected;
|
|
2718
|
-
}
|
|
2719
|
-
}
|
|
2720
|
-
let installGlobally = options.global ?? false;
|
|
2721
|
-
const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
|
|
2722
|
-
if (options.global === void 0 && !options.yes && supportsGlobal) {
|
|
2723
|
-
const scope = await ve({
|
|
2724
|
-
message: "Installation scope",
|
|
2725
|
-
options: [{
|
|
2726
|
-
value: false,
|
|
2727
|
-
label: "Project",
|
|
2728
|
-
hint: "Install in current directory (committed with your project)"
|
|
2729
|
-
}, {
|
|
2730
|
-
value: true,
|
|
2731
|
-
label: "Global",
|
|
2732
|
-
hint: "Install in home directory (available across all projects)"
|
|
2733
|
-
}]
|
|
2734
|
-
});
|
|
2735
|
-
if (pD(scope)) {
|
|
2736
|
-
xe("Installation cancelled");
|
|
2737
|
-
process.exit(0);
|
|
2738
|
-
}
|
|
2739
|
-
installGlobally = scope;
|
|
2740
|
-
}
|
|
2741
|
-
const installMode = "symlink";
|
|
2742
|
-
const cwd = process.cwd();
|
|
2743
|
-
const overwriteChecks = await Promise.all(targetAgents.map(async (agent) => ({
|
|
2744
|
-
agent,
|
|
2745
|
-
installed: await isSkillInstalled(remoteSkill.installName, agent, { global: installGlobally })
|
|
2746
|
-
})));
|
|
2747
|
-
const overwriteStatus = new Map(overwriteChecks.map(({ agent, installed }) => [agent, installed]));
|
|
2748
|
-
const summaryLines = [];
|
|
2749
|
-
targetAgents.map((a) => agents[a].displayName);
|
|
2750
|
-
const shortCanonical = shortenPath$2(getCanonicalPath(remoteSkill.installName, { global: installGlobally }), cwd);
|
|
2751
|
-
summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
|
|
2752
|
-
summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
|
|
2753
|
-
const overwriteAgents = targetAgents.filter((a) => overwriteStatus.get(a)).map((a) => agents[a].displayName);
|
|
2754
|
-
if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
|
|
2755
|
-
console.log();
|
|
2756
|
-
Me(summaryLines.join("\n"), "Installation Summary");
|
|
2757
|
-
if (!options.yes) {
|
|
2758
|
-
const confirmed = await ye({ message: "Proceed with installation?" });
|
|
2759
|
-
if (pD(confirmed) || !confirmed) {
|
|
2760
|
-
xe("Installation cancelled");
|
|
2761
|
-
process.exit(0);
|
|
2762
|
-
}
|
|
2763
|
-
}
|
|
2764
|
-
spinner.start("Installing skill...");
|
|
2765
|
-
const results = [];
|
|
2766
|
-
for (const agent of targetAgents) {
|
|
2767
|
-
const result = await installRemoteSkillForAgent(remoteSkill, agent, {
|
|
2768
|
-
global: installGlobally,
|
|
2769
|
-
mode: installMode
|
|
2770
|
-
});
|
|
2771
|
-
results.push({
|
|
2772
|
-
skill: remoteSkill.installName,
|
|
2773
|
-
agent: agents[agent].displayName,
|
|
2774
|
-
...result
|
|
2775
|
-
});
|
|
2776
|
-
}
|
|
2777
|
-
spinner.stop("Installation complete");
|
|
2778
|
-
console.log();
|
|
2779
|
-
const successful = results.filter((r) => r.success);
|
|
2780
|
-
const failed = results.filter((r) => !r.success);
|
|
2781
|
-
track({
|
|
2782
|
-
event: "install",
|
|
2783
|
-
source: "mintlify/com",
|
|
2784
|
-
skills: remoteSkill.installName,
|
|
2785
|
-
agents: targetAgents.join(","),
|
|
2786
|
-
...installGlobally && { global: "1" },
|
|
2787
|
-
skillFiles: JSON.stringify({ [remoteSkill.installName]: url }),
|
|
2788
|
-
sourceType: "mintlify"
|
|
2789
|
-
});
|
|
2790
|
-
if (successful.length > 0 && installGlobally) try {
|
|
2791
|
-
await addSkillToLock(remoteSkill.installName, {
|
|
2792
|
-
source: `mintlify/${remoteSkill.installName}`,
|
|
2793
|
-
sourceType: "mintlify",
|
|
2794
|
-
sourceUrl: url,
|
|
2795
|
-
skillFolderHash: ""
|
|
2796
|
-
});
|
|
2797
|
-
} catch {}
|
|
2798
|
-
if (successful.length > 0) {
|
|
2799
|
-
const resultLines = [];
|
|
2800
|
-
const firstResult = successful[0];
|
|
2801
|
-
if (firstResult.canonicalPath) {
|
|
2802
|
-
const shortPath = shortenPath$2(firstResult.canonicalPath, cwd);
|
|
2803
|
-
resultLines.push(`${import_picocolors.default.green("✓")} ${shortPath}`);
|
|
2804
|
-
} else resultLines.push(`${import_picocolors.default.green("✓")} ${remoteSkill.installName}`);
|
|
2805
|
-
resultLines.push(...buildResultLines(successful, targetAgents));
|
|
2806
|
-
const title = import_picocolors.default.green("Installed 1 skill");
|
|
2807
|
-
Me(resultLines.join("\n"), title);
|
|
2808
|
-
const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
|
|
2809
|
-
if (symlinkFailures.length > 0) {
|
|
2810
|
-
const copiedAgentNames = symlinkFailures.map((r) => r.agent);
|
|
2811
|
-
M.warn(import_picocolors.default.yellow(`Symlinks failed for: ${formatList$1(copiedAgentNames)}`));
|
|
2812
|
-
M.message(import_picocolors.default.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
|
|
2813
|
-
}
|
|
2814
|
-
}
|
|
2815
|
-
if (failed.length > 0) {
|
|
2816
|
-
console.log();
|
|
2817
|
-
M.error(import_picocolors.default.red(`Failed to install ${failed.length}`));
|
|
2818
|
-
for (const r of failed) M.message(` ${import_picocolors.default.red("✗")} ${r.skill} → ${r.agent}: ${import_picocolors.default.dim(r.error)}`);
|
|
2819
|
-
}
|
|
2820
|
-
console.log();
|
|
2821
|
-
Se(import_picocolors.default.green("Done!") + import_picocolors.default.dim(" Review skills before use; they run with full agent permissions."));
|
|
2822
|
-
await promptForFindSkills(options, targetAgents);
|
|
2823
|
-
}
|
|
2824
2222
|
async function runAdd(args, options = {}) {
|
|
2825
2223
|
const source = args[0];
|
|
2826
2224
|
let installTipShown = false;
|
|
@@ -2855,10 +2253,6 @@ async function runAdd(args, options = {}) {
|
|
|
2855
2253
|
spinner.start("Parsing source...");
|
|
2856
2254
|
const parsed = parseSource(source);
|
|
2857
2255
|
spinner.stop(`Source: ${parsed.type === "local" ? parsed.localPath : parsed.url}${parsed.ref ? ` @ ${import_picocolors.default.yellow(parsed.ref)}` : ""}${parsed.subpath ? ` (${parsed.subpath})` : ""}${parsed.skillFilter ? ` ${import_picocolors.default.dim("@")}${import_picocolors.default.cyan(parsed.skillFilter)}` : ""}`);
|
|
2858
|
-
if (parsed.type === "direct-url") {
|
|
2859
|
-
await handleRemoteSkill(source, parsed.url, options, spinner);
|
|
2860
|
-
return;
|
|
2861
|
-
}
|
|
2862
2256
|
if (parsed.type === "well-known") {
|
|
2863
2257
|
await handleWellKnownSkills(source, parsed.url, options, spinner);
|
|
2864
2258
|
return;
|
|
@@ -3208,7 +2602,7 @@ async function runAdd(args, options = {}) {
|
|
|
3208
2602
|
let skillFolderHash = "";
|
|
3209
2603
|
const skillPathValue = skillFiles[skill.name];
|
|
3210
2604
|
if (parsed.type === "github" && skillPathValue) {
|
|
3211
|
-
const hash = await fetchSkillFolderHash(normalizedSource, skillPathValue);
|
|
2605
|
+
const hash = await fetchSkillFolderHash(normalizedSource, skillPathValue, getGitHubToken());
|
|
3212
2606
|
if (hash) skillFolderHash = hash;
|
|
3213
2607
|
}
|
|
3214
2608
|
await addSkillToLock(skill.name, {
|
|
@@ -4473,6 +3867,22 @@ function readSkillLock() {
|
|
|
4473
3867
|
};
|
|
4474
3868
|
}
|
|
4475
3869
|
}
|
|
3870
|
+
function getSkipReason(entry) {
|
|
3871
|
+
if (entry.sourceType === "local") return "Local path";
|
|
3872
|
+
if (entry.sourceType === "git") return "Git URL (hash tracking not supported)";
|
|
3873
|
+
if (!entry.skillFolderHash) return "No version hash available";
|
|
3874
|
+
if (!entry.skillPath) return "No skill path recorded";
|
|
3875
|
+
return "No version tracking";
|
|
3876
|
+
}
|
|
3877
|
+
function printSkippedSkills(skipped) {
|
|
3878
|
+
if (skipped.length === 0) return;
|
|
3879
|
+
console.log();
|
|
3880
|
+
console.log(`${DIM}${skipped.length} skill(s) cannot be checked automatically:${RESET}`);
|
|
3881
|
+
for (const skill of skipped) {
|
|
3882
|
+
console.log(` ${TEXT}•${RESET} ${skill.name} ${DIM}(${skill.reason})${RESET}`);
|
|
3883
|
+
console.log(` ${DIM}To update: ${TEXT}npx skills add ${skill.sourceUrl} -g -y${RESET}`);
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
4476
3886
|
async function runCheck(args = []) {
|
|
4477
3887
|
console.log(`${TEXT}Checking for skill updates...${RESET}`);
|
|
4478
3888
|
console.log();
|
|
@@ -4485,12 +3895,16 @@ async function runCheck(args = []) {
|
|
|
4485
3895
|
}
|
|
4486
3896
|
const token = getGitHubToken();
|
|
4487
3897
|
const skillsBySource = /* @__PURE__ */ new Map();
|
|
4488
|
-
|
|
3898
|
+
const skipped = [];
|
|
4489
3899
|
for (const skillName of skillNames) {
|
|
4490
3900
|
const entry = lock.skills[skillName];
|
|
4491
3901
|
if (!entry) continue;
|
|
4492
|
-
if (
|
|
4493
|
-
|
|
3902
|
+
if (!entry.skillFolderHash || !entry.skillPath) {
|
|
3903
|
+
skipped.push({
|
|
3904
|
+
name: skillName,
|
|
3905
|
+
reason: getSkipReason(entry),
|
|
3906
|
+
sourceUrl: entry.sourceUrl
|
|
3907
|
+
});
|
|
4494
3908
|
continue;
|
|
4495
3909
|
}
|
|
4496
3910
|
const existing = skillsBySource.get(entry.source) || [];
|
|
@@ -4500,9 +3914,10 @@ async function runCheck(args = []) {
|
|
|
4500
3914
|
});
|
|
4501
3915
|
skillsBySource.set(entry.source, existing);
|
|
4502
3916
|
}
|
|
4503
|
-
const totalSkills = skillNames.length -
|
|
3917
|
+
const totalSkills = skillNames.length - skipped.length;
|
|
4504
3918
|
if (totalSkills === 0) {
|
|
4505
3919
|
console.log(`${DIM}No GitHub skills to check.${RESET}`);
|
|
3920
|
+
printSkippedSkills(skipped);
|
|
4506
3921
|
return;
|
|
4507
3922
|
}
|
|
4508
3923
|
console.log(`${DIM}Checking ${totalSkills} skill(s) for updates...${RESET}`);
|
|
@@ -4545,6 +3960,7 @@ async function runCheck(args = []) {
|
|
|
4545
3960
|
console.log();
|
|
4546
3961
|
console.log(`${DIM}Could not check ${errors.length} skill(s) (may need reinstall)${RESET}`);
|
|
4547
3962
|
}
|
|
3963
|
+
printSkippedSkills(skipped);
|
|
4548
3964
|
track({
|
|
4549
3965
|
event: "check",
|
|
4550
3966
|
skillCount: String(totalSkills),
|
|
@@ -4564,12 +3980,18 @@ async function runUpdate() {
|
|
|
4564
3980
|
}
|
|
4565
3981
|
const token = getGitHubToken();
|
|
4566
3982
|
const updates = [];
|
|
4567
|
-
|
|
3983
|
+
const skipped = [];
|
|
4568
3984
|
for (const skillName of skillNames) {
|
|
4569
3985
|
const entry = lock.skills[skillName];
|
|
4570
3986
|
if (!entry) continue;
|
|
4571
|
-
if (
|
|
4572
|
-
|
|
3987
|
+
if (!entry.skillFolderHash || !entry.skillPath) {
|
|
3988
|
+
skipped.push({
|
|
3989
|
+
name: skillName,
|
|
3990
|
+
reason: getSkipReason(entry),
|
|
3991
|
+
sourceUrl: entry.sourceUrl
|
|
3992
|
+
});
|
|
3993
|
+
continue;
|
|
3994
|
+
}
|
|
4573
3995
|
try {
|
|
4574
3996
|
const latestHash = await fetchSkillFolderHash(entry.source, entry.skillPath, token);
|
|
4575
3997
|
if (latestHash && latestHash !== entry.skillFolderHash) updates.push({
|
|
@@ -4579,8 +4001,9 @@ async function runUpdate() {
|
|
|
4579
4001
|
});
|
|
4580
4002
|
} catch {}
|
|
4581
4003
|
}
|
|
4582
|
-
if (
|
|
4004
|
+
if (skillNames.length - skipped.length === 0) {
|
|
4583
4005
|
console.log(`${DIM}No skills to check.${RESET}`);
|
|
4006
|
+
printSkippedSkills(skipped);
|
|
4584
4007
|
return;
|
|
4585
4008
|
}
|
|
4586
4009
|
if (updates.length === 0) {
|
|
@@ -4610,11 +4033,14 @@ async function runUpdate() {
|
|
|
4610
4033
|
installUrl,
|
|
4611
4034
|
"-g",
|
|
4612
4035
|
"-y"
|
|
4613
|
-
], {
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4036
|
+
], {
|
|
4037
|
+
stdio: [
|
|
4038
|
+
"inherit",
|
|
4039
|
+
"pipe",
|
|
4040
|
+
"pipe"
|
|
4041
|
+
],
|
|
4042
|
+
shell: process.platform === "win32"
|
|
4043
|
+
}).status === 0) {
|
|
4618
4044
|
successCount++;
|
|
4619
4045
|
console.log(` ${TEXT}✓${RESET} Updated ${update.name}`);
|
|
4620
4046
|
} else {
|