bip-skills 1.4.4 → 1.4.5
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 +379 -260
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -30,9 +30,16 @@ const DEFAULT_SEARCH_API_BASE = DEFAULT_WELL_KNOWN_BASE_URL;
|
|
|
30
30
|
const DEFAULT_GIT_HOST = "git.yonyou.com";
|
|
31
31
|
const DEFAULT_GIT_BASE_URL = `https://${DEFAULT_GIT_HOST}`;
|
|
32
32
|
const DEFAULT_GIT_FALLBACK_SSH_HOST = "git.yyrd.com";
|
|
33
|
+
const DEFAULT_INSTALL_REPORT_URL = "https://sun.yyuap.com/api/install-report";
|
|
33
34
|
function isYyuapHost(hostname) {
|
|
34
35
|
return hostname === "yyuap.com" || hostname.endsWith(".yyuap.com");
|
|
35
36
|
}
|
|
37
|
+
function isYyrdHost(hostname) {
|
|
38
|
+
return hostname === "yyrd.com" || hostname.endsWith(".yyrd.com");
|
|
39
|
+
}
|
|
40
|
+
function isInternalSkillSourceHost(hostname) {
|
|
41
|
+
return isYyuapHost(hostname) || isYyrdHost(hostname);
|
|
42
|
+
}
|
|
36
43
|
function isAllowedReportingUrl(url) {
|
|
37
44
|
try {
|
|
38
45
|
return isYyuapHost(new URL(url).hostname);
|
|
@@ -74,6 +81,20 @@ async function isRepoPrivate(owner, repo) {
|
|
|
74
81
|
return null;
|
|
75
82
|
}
|
|
76
83
|
}
|
|
84
|
+
function extractSourceHostname(sourceUrl) {
|
|
85
|
+
if (sourceUrl.startsWith("http://") || sourceUrl.startsWith("https://") || sourceUrl.startsWith("ssh://") || sourceUrl.startsWith("git://")) try {
|
|
86
|
+
return new URL(sourceUrl).hostname;
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const scpLikeMatch = sourceUrl.match(/^(?:[^@]+@)?([^:/]+):.+$/);
|
|
91
|
+
if (scpLikeMatch) return scpLikeMatch[1] ?? null;
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
function isInternalSkillSourceUrl(sourceUrl) {
|
|
95
|
+
const hostname = extractSourceHostname(sourceUrl);
|
|
96
|
+
return hostname ? isInternalSkillSourceHost(hostname) : false;
|
|
97
|
+
}
|
|
77
98
|
function isLocalPath(input) {
|
|
78
99
|
return isAbsolute(input) || input.startsWith("./") || input.startsWith("../") || input === "." || input === ".." || /^[a-zA-Z]:[/\\]/.test(input);
|
|
79
100
|
}
|
|
@@ -1400,6 +1421,7 @@ function getAllowedReportingUrl(envVarName) {
|
|
|
1400
1421
|
}
|
|
1401
1422
|
const TELEMETRY_URL = getAllowedReportingUrl("SKILLS_TELEMETRY_URL");
|
|
1402
1423
|
const AUDIT_URL = getAllowedReportingUrl("SKILLS_AUDIT_URL");
|
|
1424
|
+
const INSTALL_REPORT_URL = isAllowedReportingUrl(DEFAULT_INSTALL_REPORT_URL) ? DEFAULT_INSTALL_REPORT_URL : null;
|
|
1403
1425
|
let cliVersion = null;
|
|
1404
1426
|
function isCI() {
|
|
1405
1427
|
return !!(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.CIRCLECI || process.env.TRAVIS || process.env.BUILDKITE || process.env.JENKINS_URL || process.env.TEAMCITY_VERSION);
|
|
@@ -1428,17 +1450,24 @@ async function fetchAuditData(source, skillSlugs, timeoutMs = 3e3) {
|
|
|
1428
1450
|
return null;
|
|
1429
1451
|
}
|
|
1430
1452
|
}
|
|
1431
|
-
function
|
|
1432
|
-
if (!TELEMETRY_URL) return;
|
|
1453
|
+
function sendTelemetry(url, data) {
|
|
1433
1454
|
if (!isEnabled()) return;
|
|
1434
1455
|
try {
|
|
1435
1456
|
const params = new URLSearchParams();
|
|
1436
1457
|
if (cliVersion) params.set("v", cliVersion);
|
|
1437
1458
|
if (isCI()) params.set("ci", "1");
|
|
1438
1459
|
for (const [key, value] of Object.entries(data)) if (value !== void 0 && value !== null) params.set(key, String(value));
|
|
1439
|
-
fetch(`${
|
|
1460
|
+
fetch(`${url}?${params.toString()}`).catch(() => {});
|
|
1440
1461
|
} catch {}
|
|
1441
1462
|
}
|
|
1463
|
+
function track(data) {
|
|
1464
|
+
if (!TELEMETRY_URL) return;
|
|
1465
|
+
sendTelemetry(TELEMETRY_URL, data);
|
|
1466
|
+
}
|
|
1467
|
+
function trackInstallReport(data) {
|
|
1468
|
+
if (!INSTALL_REPORT_URL) return;
|
|
1469
|
+
sendTelemetry(INSTALL_REPORT_URL, data);
|
|
1470
|
+
}
|
|
1442
1471
|
var ProviderRegistryImpl = class {
|
|
1443
1472
|
providers = [];
|
|
1444
1473
|
register(provider) {
|
|
@@ -1804,7 +1833,7 @@ function createEmptyLocalLock() {
|
|
|
1804
1833
|
skills: {}
|
|
1805
1834
|
};
|
|
1806
1835
|
}
|
|
1807
|
-
var version$1 = "1.4.
|
|
1836
|
+
var version$1 = "1.4.5";
|
|
1808
1837
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
1809
1838
|
async function isSourcePrivate(source) {
|
|
1810
1839
|
const ownerRepo = parseOwnerRepo(source);
|
|
@@ -1970,6 +1999,33 @@ async function selectAgentsInteractive(options) {
|
|
|
1970
1999
|
return selected;
|
|
1971
2000
|
}
|
|
1972
2001
|
setVersion(version$1);
|
|
2002
|
+
function resolveInstallOutcome(successCount, failCount) {
|
|
2003
|
+
if (failCount === 0) return "success";
|
|
2004
|
+
if (successCount === 0) return "failure";
|
|
2005
|
+
return "partial_failure";
|
|
2006
|
+
}
|
|
2007
|
+
function buildFailedDetails(results) {
|
|
2008
|
+
const failed = results.filter((result) => !result.success).map((result) => `${result.skill}@${result.agent}${result.error ? `:${result.error}` : ""}`);
|
|
2009
|
+
return failed.length > 0 ? JSON.stringify(failed) : void 0;
|
|
2010
|
+
}
|
|
2011
|
+
function reportInstallToSunIfNeeded(params) {
|
|
2012
|
+
if (!isInternalSkillSourceUrl(params.sourceUrl)) return;
|
|
2013
|
+
trackInstallReport({
|
|
2014
|
+
event: "install",
|
|
2015
|
+
source: params.source,
|
|
2016
|
+
sourceUrl: params.sourceUrl,
|
|
2017
|
+
skills: params.skills.join(","),
|
|
2018
|
+
agents: params.agents.join(","),
|
|
2019
|
+
...params.installGlobally && { global: "1" },
|
|
2020
|
+
...params.skillFiles && { skillFiles: JSON.stringify(params.skillFiles) },
|
|
2021
|
+
...params.sourceType && { sourceType: params.sourceType },
|
|
2022
|
+
result: resolveInstallOutcome(params.successfulCount, params.failedCount),
|
|
2023
|
+
successCount: String(params.successfulCount),
|
|
2024
|
+
failCount: String(params.failedCount),
|
|
2025
|
+
...params.failedDetails && { failedDetails: params.failedDetails },
|
|
2026
|
+
...params.errorMessage && { errorMessage: params.errorMessage }
|
|
2027
|
+
});
|
|
2028
|
+
}
|
|
1973
2029
|
function isBareSkillNameSource(source) {
|
|
1974
2030
|
return /^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/.test(source);
|
|
1975
2031
|
}
|
|
@@ -1980,297 +2036,327 @@ function buildFallbackGitSshUrl(source) {
|
|
|
1980
2036
|
return `git@${DEFAULT_GIT_FALLBACK_SSH_HOST}:${source.replace(/\.git$/, "")}.git`;
|
|
1981
2037
|
}
|
|
1982
2038
|
async function handleWellKnownSkills(source, url, options, spinner, allowFallback = false, fallbackToRoot = true) {
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
return false;
|
|
1989
|
-
}
|
|
1990
|
-
spinner.stop(import_picocolors.default.red("No skills found"));
|
|
1991
|
-
Se(import_picocolors.default.red("No skills found at this URL. Make sure the server has a /.well-known/skills/index.json file."));
|
|
1992
|
-
process.exit(1);
|
|
1993
|
-
}
|
|
1994
|
-
spinner.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
|
|
1995
|
-
for (const skill of skills) {
|
|
1996
|
-
M.info(`Skill: ${import_picocolors.default.cyan(skill.installName)}`);
|
|
1997
|
-
M.message(import_picocolors.default.dim(skill.description));
|
|
1998
|
-
if (skill.files.size > 1) M.message(import_picocolors.default.dim(` Files: ${Array.from(skill.files.keys()).join(", ")}`));
|
|
1999
|
-
}
|
|
2000
|
-
if (options.list) {
|
|
2001
|
-
console.log();
|
|
2002
|
-
M.step(import_picocolors.default.bold("Available Skills"));
|
|
2003
|
-
for (const skill of skills) {
|
|
2004
|
-
M.message(` ${import_picocolors.default.cyan(skill.installName)}`);
|
|
2005
|
-
M.message(` ${import_picocolors.default.dim(skill.description)}`);
|
|
2006
|
-
if (skill.files.size > 1) M.message(` ${import_picocolors.default.dim(`Files: ${skill.files.size}`)}`);
|
|
2007
|
-
}
|
|
2008
|
-
console.log();
|
|
2009
|
-
Se("Run without --list to install");
|
|
2010
|
-
process.exit(0);
|
|
2011
|
-
}
|
|
2012
|
-
let selectedSkills;
|
|
2013
|
-
if (options.skill?.includes("*")) {
|
|
2014
|
-
selectedSkills = skills;
|
|
2015
|
-
M.info(`Installing all ${skills.length} skills`);
|
|
2016
|
-
} else if (options.skill && options.skill.length > 0) {
|
|
2017
|
-
selectedSkills = skills.filter((s) => options.skill.some((name) => s.installName.toLowerCase() === name.toLowerCase() || s.name.toLowerCase() === name.toLowerCase()));
|
|
2018
|
-
if (selectedSkills.length === 0) {
|
|
2039
|
+
const shouldReportToSun = isInternalSkillSourceUrl(url);
|
|
2040
|
+
try {
|
|
2041
|
+
spinner.start("Discovering skills from well-known endpoint...");
|
|
2042
|
+
const skills = await wellKnownProvider.fetchAllSkills(url, { fallbackToRoot });
|
|
2043
|
+
if (skills.length === 0) {
|
|
2019
2044
|
if (allowFallback) {
|
|
2020
|
-
|
|
2045
|
+
spinner.stop(import_picocolors.default.yellow("Default well-known endpoint unavailable"));
|
|
2021
2046
|
return false;
|
|
2022
2047
|
}
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
for (const s of skills) M.message(` - ${s.installName}`);
|
|
2048
|
+
spinner.stop(import_picocolors.default.red("No skills found"));
|
|
2049
|
+
Se(import_picocolors.default.red("No skills found at this URL. Make sure the server has a /.well-known/skills/index.json file."));
|
|
2026
2050
|
process.exit(1);
|
|
2027
2051
|
}
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
selectedSkills = skills;
|
|
2034
|
-
M.info(`Installing all ${skills.length} skills`);
|
|
2035
|
-
} else {
|
|
2036
|
-
const selected = await multiselect({
|
|
2037
|
-
message: "Select skills to install",
|
|
2038
|
-
options: skills.map((s) => ({
|
|
2039
|
-
value: s,
|
|
2040
|
-
label: s.installName,
|
|
2041
|
-
hint: s.description.length > 60 ? s.description.slice(0, 57) + "..." : s.description
|
|
2042
|
-
})),
|
|
2043
|
-
required: true
|
|
2044
|
-
});
|
|
2045
|
-
if (pD(selected)) {
|
|
2046
|
-
xe("Installation cancelled");
|
|
2047
|
-
process.exit(0);
|
|
2052
|
+
spinner.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
|
|
2053
|
+
for (const skill of skills) {
|
|
2054
|
+
M.info(`Skill: ${import_picocolors.default.cyan(skill.installName)}`);
|
|
2055
|
+
M.message(import_picocolors.default.dim(skill.description));
|
|
2056
|
+
if (skill.files.size > 1) M.message(import_picocolors.default.dim(` Files: ${Array.from(skill.files.keys()).join(", ")}`));
|
|
2048
2057
|
}
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
M.info(`Valid agents: ${validAgents.join(", ")}`);
|
|
2061
|
-
process.exit(1);
|
|
2058
|
+
if (options.list) {
|
|
2059
|
+
console.log();
|
|
2060
|
+
M.step(import_picocolors.default.bold("Available Skills"));
|
|
2061
|
+
for (const skill of skills) {
|
|
2062
|
+
M.message(` ${import_picocolors.default.cyan(skill.installName)}`);
|
|
2063
|
+
M.message(` ${import_picocolors.default.dim(skill.description)}`);
|
|
2064
|
+
if (skill.files.size > 1) M.message(` ${import_picocolors.default.dim(`Files: ${skill.files.size}`)}`);
|
|
2065
|
+
}
|
|
2066
|
+
console.log();
|
|
2067
|
+
Se("Run without --list to install");
|
|
2068
|
+
process.exit(0);
|
|
2062
2069
|
}
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2070
|
+
let selectedSkills;
|
|
2071
|
+
if (options.skill?.includes("*")) {
|
|
2072
|
+
selectedSkills = skills;
|
|
2073
|
+
M.info(`Installing all ${skills.length} skills`);
|
|
2074
|
+
} else if (options.skill && options.skill.length > 0) {
|
|
2075
|
+
selectedSkills = skills.filter((s) => options.skill.some((name) => s.installName.toLowerCase() === name.toLowerCase() || s.name.toLowerCase() === name.toLowerCase()));
|
|
2076
|
+
if (selectedSkills.length === 0) {
|
|
2077
|
+
if (allowFallback) {
|
|
2078
|
+
M.info(`Skill not found on default well-known endpoint: ${options.skill.join(", ")}`);
|
|
2079
|
+
return false;
|
|
2080
|
+
}
|
|
2081
|
+
M.error(`No matching skills found for: ${options.skill.join(", ")}`);
|
|
2082
|
+
M.info("Available skills:");
|
|
2083
|
+
for (const s of skills) M.message(` - ${s.installName}`);
|
|
2084
|
+
process.exit(1);
|
|
2085
|
+
}
|
|
2086
|
+
} else if (skills.length === 1) {
|
|
2087
|
+
selectedSkills = skills;
|
|
2088
|
+
const firstSkill = skills[0];
|
|
2089
|
+
M.info(`Skill: ${import_picocolors.default.cyan(firstSkill.installName)}`);
|
|
2090
|
+
} else if (options.yes) {
|
|
2091
|
+
selectedSkills = skills;
|
|
2092
|
+
M.info(`Installing all ${skills.length} skills`);
|
|
2072
2093
|
} else {
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2094
|
+
const selected = await multiselect({
|
|
2095
|
+
message: "Select skills to install",
|
|
2096
|
+
options: skills.map((s) => ({
|
|
2097
|
+
value: s,
|
|
2098
|
+
label: s.installName,
|
|
2099
|
+
hint: s.description.length > 60 ? s.description.slice(0, 57) + "..." : s.description
|
|
2100
|
+
})),
|
|
2101
|
+
required: true
|
|
2102
|
+
});
|
|
2078
2103
|
if (pD(selected)) {
|
|
2079
2104
|
xe("Installation cancelled");
|
|
2080
2105
|
process.exit(0);
|
|
2081
2106
|
}
|
|
2082
|
-
|
|
2107
|
+
selectedSkills = selected;
|
|
2083
2108
|
}
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2109
|
+
let targetAgents;
|
|
2110
|
+
const validAgents = Object.keys(agents);
|
|
2111
|
+
if (options.agent?.includes("*")) {
|
|
2112
|
+
targetAgents = validAgents;
|
|
2113
|
+
M.info(`Installing to all ${targetAgents.length} agents`);
|
|
2114
|
+
} else if (options.agent && options.agent.length > 0) {
|
|
2115
|
+
const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
|
|
2116
|
+
if (invalidAgents.length > 0) {
|
|
2117
|
+
M.error(`Invalid agents: ${invalidAgents.join(", ")}`);
|
|
2118
|
+
M.info(`Valid agents: ${validAgents.join(", ")}`);
|
|
2119
|
+
process.exit(1);
|
|
2120
|
+
}
|
|
2121
|
+
targetAgents = options.agent;
|
|
2090
2122
|
} else {
|
|
2091
|
-
|
|
2092
|
-
|
|
2123
|
+
spinner.start("Loading agents...");
|
|
2124
|
+
const installedAgents = await detectInstalledAgents();
|
|
2125
|
+
const totalAgents = Object.keys(agents).length;
|
|
2126
|
+
spinner.stop(`${totalAgents} agents`);
|
|
2127
|
+
if (installedAgents.length === 0) if (options.yes) {
|
|
2128
|
+
targetAgents = validAgents;
|
|
2129
|
+
M.info("Installing to all agents");
|
|
2130
|
+
} else {
|
|
2131
|
+
M.info("Select agents to install skills to");
|
|
2132
|
+
const selected = await promptForAgents("Which agents do you want to install to?", Object.entries(agents).map(([key, config]) => ({
|
|
2133
|
+
value: key,
|
|
2134
|
+
label: config.displayName
|
|
2135
|
+
})));
|
|
2136
|
+
if (pD(selected)) {
|
|
2137
|
+
xe("Installation cancelled");
|
|
2138
|
+
process.exit(0);
|
|
2139
|
+
}
|
|
2140
|
+
targetAgents = selected;
|
|
2141
|
+
}
|
|
2142
|
+
else if (installedAgents.length === 1 || options.yes) {
|
|
2143
|
+
targetAgents = ensureUniversalAgents(installedAgents);
|
|
2144
|
+
if (installedAgents.length === 1) {
|
|
2145
|
+
const firstAgent = installedAgents[0];
|
|
2146
|
+
M.info(`Installing to: ${import_picocolors.default.cyan(agents[firstAgent].displayName)}`);
|
|
2147
|
+
} else M.info(`Installing to: ${installedAgents.map((a) => import_picocolors.default.cyan(agents[a].displayName)).join(", ")}`);
|
|
2148
|
+
} else {
|
|
2149
|
+
const selected = await selectAgentsInteractive({ global: options.global });
|
|
2150
|
+
if (pD(selected)) {
|
|
2151
|
+
xe("Installation cancelled");
|
|
2152
|
+
process.exit(0);
|
|
2153
|
+
}
|
|
2154
|
+
targetAgents = selected;
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
let installGlobally = options.global ?? false;
|
|
2158
|
+
const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
|
|
2159
|
+
if (options.global === void 0 && !options.yes && supportsGlobal) {
|
|
2160
|
+
const scope = await ve({
|
|
2161
|
+
message: "Installation scope",
|
|
2162
|
+
options: [{
|
|
2163
|
+
value: false,
|
|
2164
|
+
label: "Project",
|
|
2165
|
+
hint: "Install in current directory (committed with your project)"
|
|
2166
|
+
}, {
|
|
2167
|
+
value: true,
|
|
2168
|
+
label: "Global",
|
|
2169
|
+
hint: "Install in home directory (available across all projects)"
|
|
2170
|
+
}]
|
|
2171
|
+
});
|
|
2172
|
+
if (pD(scope)) {
|
|
2093
2173
|
xe("Installation cancelled");
|
|
2094
2174
|
process.exit(0);
|
|
2095
2175
|
}
|
|
2096
|
-
|
|
2176
|
+
installGlobally = scope;
|
|
2097
2177
|
}
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2178
|
+
let installMode = options.copy ? "copy" : "symlink";
|
|
2179
|
+
if (!options.copy && !options.yes) {
|
|
2180
|
+
const modeChoice = await ve({
|
|
2181
|
+
message: "Installation method",
|
|
2182
|
+
options: [{
|
|
2183
|
+
value: "symlink",
|
|
2184
|
+
label: "Symlink (Recommended)",
|
|
2185
|
+
hint: "Single source of truth, easy updates"
|
|
2186
|
+
}, {
|
|
2187
|
+
value: "copy",
|
|
2188
|
+
label: "Copy to all agents",
|
|
2189
|
+
hint: "Independent copies for each agent"
|
|
2190
|
+
}]
|
|
2191
|
+
});
|
|
2192
|
+
if (pD(modeChoice)) {
|
|
2193
|
+
xe("Installation cancelled");
|
|
2194
|
+
process.exit(0);
|
|
2195
|
+
}
|
|
2196
|
+
installMode = modeChoice;
|
|
2117
2197
|
}
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
label: "Copy to all agents",
|
|
2131
|
-
hint: "Independent copies for each agent"
|
|
2132
|
-
}]
|
|
2133
|
-
});
|
|
2134
|
-
if (pD(modeChoice)) {
|
|
2135
|
-
xe("Installation cancelled");
|
|
2136
|
-
process.exit(0);
|
|
2198
|
+
const cwd = process.cwd();
|
|
2199
|
+
const summaryLines = [];
|
|
2200
|
+
targetAgents.map((a) => agents[a].displayName);
|
|
2201
|
+
const overwriteChecks = await Promise.all(selectedSkills.flatMap((skill) => targetAgents.map(async (agent) => ({
|
|
2202
|
+
skillName: skill.installName,
|
|
2203
|
+
agent,
|
|
2204
|
+
installed: await isSkillInstalled(skill.installName, agent, { global: installGlobally })
|
|
2205
|
+
}))));
|
|
2206
|
+
const overwriteStatus = /* @__PURE__ */ new Map();
|
|
2207
|
+
for (const { skillName, agent, installed } of overwriteChecks) {
|
|
2208
|
+
if (!overwriteStatus.has(skillName)) overwriteStatus.set(skillName, /* @__PURE__ */ new Map());
|
|
2209
|
+
overwriteStatus.get(skillName).set(agent, installed);
|
|
2137
2210
|
}
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
}))));
|
|
2148
|
-
const overwriteStatus = /* @__PURE__ */ new Map();
|
|
2149
|
-
for (const { skillName, agent, installed } of overwriteChecks) {
|
|
2150
|
-
if (!overwriteStatus.has(skillName)) overwriteStatus.set(skillName, /* @__PURE__ */ new Map());
|
|
2151
|
-
overwriteStatus.get(skillName).set(agent, installed);
|
|
2152
|
-
}
|
|
2153
|
-
for (const skill of selectedSkills) {
|
|
2154
|
-
if (summaryLines.length > 0) summaryLines.push("");
|
|
2155
|
-
const shortCanonical = shortenPath$2(getCanonicalPath(skill.installName, { global: installGlobally }), cwd);
|
|
2156
|
-
summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
|
|
2157
|
-
summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
|
|
2158
|
-
if (skill.files.size > 1) summaryLines.push(` ${import_picocolors.default.dim("files:")} ${skill.files.size}`);
|
|
2159
|
-
const skillOverwrites = overwriteStatus.get(skill.installName);
|
|
2160
|
-
const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
|
|
2161
|
-
if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
|
|
2162
|
-
}
|
|
2163
|
-
console.log();
|
|
2164
|
-
Me(summaryLines.join("\n"), "Installation Summary");
|
|
2165
|
-
if (!options.yes) {
|
|
2166
|
-
const confirmed = await ye({ message: "Proceed with installation?" });
|
|
2167
|
-
if (pD(confirmed) || !confirmed) {
|
|
2168
|
-
xe("Installation cancelled");
|
|
2169
|
-
process.exit(0);
|
|
2211
|
+
for (const skill of selectedSkills) {
|
|
2212
|
+
if (summaryLines.length > 0) summaryLines.push("");
|
|
2213
|
+
const shortCanonical = shortenPath$2(getCanonicalPath(skill.installName, { global: installGlobally }), cwd);
|
|
2214
|
+
summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
|
|
2215
|
+
summaryLines.push(...buildAgentSummaryLines(targetAgents, installMode));
|
|
2216
|
+
if (skill.files.size > 1) summaryLines.push(` ${import_picocolors.default.dim("files:")} ${skill.files.size}`);
|
|
2217
|
+
const skillOverwrites = overwriteStatus.get(skill.installName);
|
|
2218
|
+
const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
|
|
2219
|
+
if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$1(overwriteAgents)}`);
|
|
2170
2220
|
}
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2221
|
+
console.log();
|
|
2222
|
+
Me(summaryLines.join("\n"), "Installation Summary");
|
|
2223
|
+
if (!options.yes) {
|
|
2224
|
+
const confirmed = await ye({ message: "Proceed with installation?" });
|
|
2225
|
+
if (pD(confirmed) || !confirmed) {
|
|
2226
|
+
xe("Installation cancelled");
|
|
2227
|
+
process.exit(0);
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
spinner.start("Installing skills...");
|
|
2231
|
+
const results = [];
|
|
2232
|
+
for (const skill of selectedSkills) for (const agent of targetAgents) {
|
|
2233
|
+
const result = await installWellKnownSkillForAgent(skill, agent, {
|
|
2234
|
+
global: installGlobally,
|
|
2235
|
+
mode: installMode
|
|
2236
|
+
});
|
|
2237
|
+
results.push({
|
|
2238
|
+
skill: skill.installName,
|
|
2239
|
+
agent: agents[agent].displayName,
|
|
2240
|
+
...result
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
spinner.stop("Installation complete");
|
|
2244
|
+
console.log();
|
|
2245
|
+
const successful = results.filter((r) => r.success);
|
|
2246
|
+
const failed = results.filter((r) => !r.success);
|
|
2247
|
+
const sourceIdentifier = wellKnownProvider.getSourceIdentifier(url);
|
|
2248
|
+
const skillFiles = {};
|
|
2249
|
+
for (const skill of selectedSkills) skillFiles[skill.installName] = skill.sourceUrl;
|
|
2250
|
+
if (await isSourcePrivate(sourceIdentifier) !== true) track({
|
|
2251
|
+
event: "install",
|
|
2252
|
+
source: sourceIdentifier,
|
|
2253
|
+
skills: selectedSkills.map((s) => s.installName).join(","),
|
|
2254
|
+
agents: targetAgents.join(","),
|
|
2255
|
+
...installGlobally && { global: "1" },
|
|
2256
|
+
skillFiles: JSON.stringify(skillFiles),
|
|
2257
|
+
sourceType: "well-known"
|
|
2178
2258
|
});
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2259
|
+
reportInstallToSunIfNeeded({
|
|
2260
|
+
source: sourceIdentifier,
|
|
2261
|
+
sourceUrl: url,
|
|
2262
|
+
skills: selectedSkills.map((s) => s.installName),
|
|
2263
|
+
agents: targetAgents,
|
|
2264
|
+
installGlobally,
|
|
2265
|
+
skillFiles,
|
|
2266
|
+
sourceType: "well-known",
|
|
2267
|
+
successfulCount: successful.length,
|
|
2268
|
+
failedCount: failed.length,
|
|
2269
|
+
failedDetails: buildFailedDetails(results)
|
|
2183
2270
|
});
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
const failed = results.filter((r) => !r.success);
|
|
2189
|
-
const sourceIdentifier = wellKnownProvider.getSourceIdentifier(url);
|
|
2190
|
-
const skillFiles = {};
|
|
2191
|
-
for (const skill of selectedSkills) skillFiles[skill.installName] = skill.sourceUrl;
|
|
2192
|
-
if (await isSourcePrivate(sourceIdentifier) !== true) track({
|
|
2193
|
-
event: "install",
|
|
2194
|
-
source: sourceIdentifier,
|
|
2195
|
-
skills: selectedSkills.map((s) => s.installName).join(","),
|
|
2196
|
-
agents: targetAgents.join(","),
|
|
2197
|
-
...installGlobally && { global: "1" },
|
|
2198
|
-
skillFiles: JSON.stringify(skillFiles),
|
|
2199
|
-
sourceType: "well-known"
|
|
2200
|
-
});
|
|
2201
|
-
if (successful.length > 0 && installGlobally) {
|
|
2202
|
-
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
2203
|
-
for (const skill of selectedSkills) if (successfulSkillNames.has(skill.installName)) try {
|
|
2204
|
-
await addSkillToLock(skill.installName, {
|
|
2205
|
-
source: sourceIdentifier,
|
|
2206
|
-
sourceType: "well-known",
|
|
2207
|
-
sourceUrl: skill.sourceUrl,
|
|
2208
|
-
skillFolderHash: ""
|
|
2209
|
-
});
|
|
2210
|
-
} catch {}
|
|
2211
|
-
}
|
|
2212
|
-
if (successful.length > 0 && !installGlobally) {
|
|
2213
|
-
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
2214
|
-
for (const skill of selectedSkills) if (successfulSkillNames.has(skill.installName)) try {
|
|
2215
|
-
const matchingResult = successful.find((r) => r.skill === skill.installName);
|
|
2216
|
-
const installDir = matchingResult?.canonicalPath || matchingResult?.path;
|
|
2217
|
-
if (installDir) {
|
|
2218
|
-
const computedHash = await computeSkillFolderHash(installDir);
|
|
2219
|
-
await addSkillToLocalLock(skill.installName, {
|
|
2271
|
+
if (successful.length > 0 && installGlobally) {
|
|
2272
|
+
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
2273
|
+
for (const skill of selectedSkills) if (successfulSkillNames.has(skill.installName)) try {
|
|
2274
|
+
await addSkillToLock(skill.installName, {
|
|
2220
2275
|
source: sourceIdentifier,
|
|
2221
2276
|
sourceType: "well-known",
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
}
|
|
2227
|
-
if (successful.length > 0) {
|
|
2228
|
-
const bySkill = /* @__PURE__ */ new Map();
|
|
2229
|
-
for (const r of successful) {
|
|
2230
|
-
const skillResults = bySkill.get(r.skill) || [];
|
|
2231
|
-
skillResults.push(r);
|
|
2232
|
-
bySkill.set(r.skill, skillResults);
|
|
2277
|
+
sourceUrl: skill.sourceUrl,
|
|
2278
|
+
skillFolderHash: ""
|
|
2279
|
+
});
|
|
2280
|
+
} catch {}
|
|
2233
2281
|
}
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2282
|
+
if (successful.length > 0 && !installGlobally) {
|
|
2283
|
+
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
2284
|
+
for (const skill of selectedSkills) if (successfulSkillNames.has(skill.installName)) try {
|
|
2285
|
+
const matchingResult = successful.find((r) => r.skill === skill.installName);
|
|
2286
|
+
const installDir = matchingResult?.canonicalPath || matchingResult?.path;
|
|
2287
|
+
if (installDir) {
|
|
2288
|
+
const computedHash = await computeSkillFolderHash(installDir);
|
|
2289
|
+
await addSkillToLocalLock(skill.installName, {
|
|
2290
|
+
source: sourceIdentifier,
|
|
2291
|
+
sourceType: "well-known",
|
|
2292
|
+
computedHash
|
|
2293
|
+
}, cwd);
|
|
2245
2294
|
}
|
|
2246
|
-
}
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2295
|
+
} catch {}
|
|
2296
|
+
}
|
|
2297
|
+
if (successful.length > 0) {
|
|
2298
|
+
const bySkill = /* @__PURE__ */ new Map();
|
|
2299
|
+
for (const r of successful) {
|
|
2300
|
+
const skillResults = bySkill.get(r.skill) || [];
|
|
2301
|
+
skillResults.push(r);
|
|
2302
|
+
bySkill.set(r.skill, skillResults);
|
|
2303
|
+
}
|
|
2304
|
+
const skillCount = bySkill.size;
|
|
2305
|
+
const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
|
|
2306
|
+
const copiedAgents = symlinkFailures.map((r) => r.agent);
|
|
2307
|
+
const resultLines = [];
|
|
2308
|
+
for (const [skillName, skillResults] of bySkill) {
|
|
2309
|
+
const firstResult = skillResults[0];
|
|
2310
|
+
if (firstResult.mode === "copy") {
|
|
2311
|
+
resultLines.push(`${import_picocolors.default.green("✓")} ${skillName} ${import_picocolors.default.dim("(copied)")}`);
|
|
2312
|
+
for (const r of skillResults) {
|
|
2313
|
+
const shortPath = shortenPath$2(r.path, cwd);
|
|
2314
|
+
resultLines.push(` ${import_picocolors.default.dim("→")} ${shortPath}`);
|
|
2315
|
+
}
|
|
2316
|
+
} else {
|
|
2317
|
+
if (firstResult.canonicalPath) {
|
|
2318
|
+
const shortPath = shortenPath$2(firstResult.canonicalPath, cwd);
|
|
2319
|
+
resultLines.push(`${import_picocolors.default.green("✓")} ${shortPath}`);
|
|
2320
|
+
} else resultLines.push(`${import_picocolors.default.green("✓")} ${skillName}`);
|
|
2321
|
+
resultLines.push(...buildResultLines(skillResults, targetAgents));
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
const title = import_picocolors.default.green(`Installed ${skillCount} skill${skillCount !== 1 ? "s" : ""}`);
|
|
2325
|
+
Me(resultLines.join("\n"), title);
|
|
2326
|
+
if (symlinkFailures.length > 0) {
|
|
2327
|
+
M.warn(import_picocolors.default.yellow(`Symlinks failed for: ${formatList$1(copiedAgents)}`));
|
|
2328
|
+
M.message(import_picocolors.default.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
|
|
2252
2329
|
}
|
|
2253
2330
|
}
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
M.
|
|
2258
|
-
M.message(import_picocolors.default.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
|
|
2331
|
+
if (failed.length > 0) {
|
|
2332
|
+
console.log();
|
|
2333
|
+
M.error(import_picocolors.default.red(`Failed to install ${failed.length}`));
|
|
2334
|
+
for (const r of failed) M.message(` ${import_picocolors.default.red("✗")} ${r.skill} → ${r.agent}: ${import_picocolors.default.dim(r.error)}`);
|
|
2259
2335
|
}
|
|
2260
|
-
}
|
|
2261
|
-
if (failed.length > 0) {
|
|
2262
2336
|
console.log();
|
|
2263
|
-
|
|
2264
|
-
|
|
2337
|
+
Se(import_picocolors.default.green("Done!") + import_picocolors.default.dim(" Review skills before use; they run with full agent permissions."));
|
|
2338
|
+
await promptForFindSkills(options, targetAgents);
|
|
2339
|
+
return true;
|
|
2340
|
+
} catch (error) {
|
|
2341
|
+
if (shouldReportToSun) reportInstallToSunIfNeeded({
|
|
2342
|
+
source: wellKnownProvider.getSourceIdentifier(url),
|
|
2343
|
+
sourceUrl: url,
|
|
2344
|
+
skills: options.skill || [],
|
|
2345
|
+
agents: options.agent || [],
|
|
2346
|
+
installGlobally: options.global ?? false,
|
|
2347
|
+
sourceType: "well-known",
|
|
2348
|
+
successfulCount: 0,
|
|
2349
|
+
failedCount: 1,
|
|
2350
|
+
errorMessage: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2351
|
+
});
|
|
2352
|
+
throw error;
|
|
2265
2353
|
}
|
|
2266
|
-
console.log();
|
|
2267
|
-
Se(import_picocolors.default.green("Done!") + import_picocolors.default.dim(" Review skills before use; they run with full agent permissions."));
|
|
2268
|
-
await promptForFindSkills(options, targetAgents);
|
|
2269
|
-
return true;
|
|
2270
2354
|
}
|
|
2271
2355
|
async function runAdd(args, options = {}) {
|
|
2272
2356
|
let source = args[0] || DEFAULT_WELL_KNOWN_BASE_URL;
|
|
2273
2357
|
let installTipShown = false;
|
|
2358
|
+
let parsed = null;
|
|
2359
|
+
let installReportContext = null;
|
|
2274
2360
|
const showInstallTip = () => {
|
|
2275
2361
|
if (installTipShown) return;
|
|
2276
2362
|
M.message(import_picocolors.default.dim("Tip: use the --yes (-y) and --global (-g) flags to install without prompts."));
|
|
@@ -2300,7 +2386,7 @@ async function runAdd(args, options = {}) {
|
|
|
2300
2386
|
M.info(`Falling back to git source: ${import_picocolors.default.cyan(source)}`);
|
|
2301
2387
|
}
|
|
2302
2388
|
spinner.start("Parsing source...");
|
|
2303
|
-
|
|
2389
|
+
parsed = parseSource(source);
|
|
2304
2390
|
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)}` : ""}`);
|
|
2305
2391
|
if (parsed.skillFilter) {
|
|
2306
2392
|
options.skill = options.skill || [];
|
|
@@ -2623,6 +2709,15 @@ async function runAdd(args, options = {}) {
|
|
|
2623
2709
|
skillFiles[skill.name] = relativePath;
|
|
2624
2710
|
}
|
|
2625
2711
|
const normalizedSource = getOwnerRepo(parsed);
|
|
2712
|
+
installReportContext = {
|
|
2713
|
+
source: normalizedSource || parsed.url,
|
|
2714
|
+
sourceUrl: parsed.url,
|
|
2715
|
+
skills: selectedSkills.map((s) => s.name),
|
|
2716
|
+
agents: targetAgents,
|
|
2717
|
+
installGlobally,
|
|
2718
|
+
sourceType: parsed.type,
|
|
2719
|
+
skillFiles
|
|
2720
|
+
};
|
|
2626
2721
|
if (normalizedSource) {
|
|
2627
2722
|
const ownerRepo = parseOwnerRepo(normalizedSource);
|
|
2628
2723
|
if (ownerRepo) {
|
|
@@ -2643,6 +2738,18 @@ async function runAdd(args, options = {}) {
|
|
|
2643
2738
|
skillFiles: JSON.stringify(skillFiles)
|
|
2644
2739
|
});
|
|
2645
2740
|
}
|
|
2741
|
+
reportInstallToSunIfNeeded({
|
|
2742
|
+
source: installReportContext.source,
|
|
2743
|
+
sourceUrl: installReportContext.sourceUrl,
|
|
2744
|
+
skills: installReportContext.skills,
|
|
2745
|
+
agents: installReportContext.agents,
|
|
2746
|
+
installGlobally: installReportContext.installGlobally,
|
|
2747
|
+
sourceType: installReportContext.sourceType,
|
|
2748
|
+
skillFiles: installReportContext.skillFiles,
|
|
2749
|
+
successfulCount: successful.length,
|
|
2750
|
+
failedCount: failed.length,
|
|
2751
|
+
failedDetails: buildFailedDetails(results)
|
|
2752
|
+
});
|
|
2646
2753
|
if (successful.length > 0 && installGlobally && normalizedSource) {
|
|
2647
2754
|
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
2648
2755
|
for (const skill of selectedSkills) {
|
|
@@ -2746,6 +2853,18 @@ async function runAdd(args, options = {}) {
|
|
|
2746
2853
|
Se(import_picocolors.default.green("Done!") + import_picocolors.default.dim(" Review skills before use; they run with full agent permissions."));
|
|
2747
2854
|
await promptForFindSkills(options, targetAgents);
|
|
2748
2855
|
} catch (error) {
|
|
2856
|
+
if (parsed) reportInstallToSunIfNeeded({
|
|
2857
|
+
source: getOwnerRepo(parsed) || parsed.url,
|
|
2858
|
+
sourceUrl: parsed.url,
|
|
2859
|
+
skills: installReportContext?.skills || [],
|
|
2860
|
+
agents: installReportContext?.agents || [],
|
|
2861
|
+
installGlobally: installReportContext?.installGlobally || false,
|
|
2862
|
+
sourceType: installReportContext?.sourceType || parsed.type,
|
|
2863
|
+
skillFiles: installReportContext?.skillFiles,
|
|
2864
|
+
successfulCount: 0,
|
|
2865
|
+
failedCount: 1,
|
|
2866
|
+
errorMessage: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2867
|
+
});
|
|
2749
2868
|
if (error instanceof GitCloneError) {
|
|
2750
2869
|
M.error(import_picocolors.default.red("Failed to clone repository"));
|
|
2751
2870
|
for (const line of error.message.split("\n")) M.message(import_picocolors.default.dim(line));
|