nuxt-skill-hub 0.0.7 → 0.0.8
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/module.d.mts +2 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +584 -167
- package/package.json +4 -2
package/dist/module.d.mts
CHANGED
|
@@ -2,8 +2,8 @@ import * as _nuxt_schema from '@nuxt/schema';
|
|
|
2
2
|
|
|
3
3
|
type SkillHubTarget = string;
|
|
4
4
|
|
|
5
|
-
type SkillSourceKind = 'dist' | 'github' | 'generated';
|
|
6
|
-
type SkillResolverKind = 'agentsField' | 'githubHeuristic' | 'metadataRouter';
|
|
5
|
+
type SkillSourceKind = 'dist' | 'github' | 'wellKnown' | 'generated';
|
|
6
|
+
type SkillResolverKind = 'agentsField' | 'githubHeuristic' | 'wellKnownV2' | 'metadataRouter';
|
|
7
7
|
|
|
8
8
|
type SkillHubGenerationMode = 'prepare' | 'manual';
|
|
9
9
|
interface ModuleOptions {
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -8,15 +8,16 @@ import matter from 'gray-matter';
|
|
|
8
8
|
import { readPackageJSON, findWorkspaceDir, resolvePackageJSON, resolveLockfile } from 'pkg-types';
|
|
9
9
|
import { transform } from 'automd';
|
|
10
10
|
import { downloadTemplate } from 'giget';
|
|
11
|
-
import { detectInstalledAgents, expandPath, detectCurrentAgent, getAgentIds
|
|
11
|
+
import { getAgentConfig, detectInstalledAgents, expandPath, detectCurrentAgent, getAgentIds } from 'unagent/env';
|
|
12
12
|
import { ofetch } from 'ofetch';
|
|
13
13
|
import { createHash } from 'node:crypto';
|
|
14
|
+
import { isIP } from 'node:net';
|
|
14
15
|
import { hash } from 'ohash';
|
|
15
16
|
import { glob } from 'tinyglobby';
|
|
16
17
|
import { createConsola } from 'consola';
|
|
17
18
|
import { colorize } from 'consola/utils';
|
|
18
19
|
|
|
19
|
-
const version = "0.0.
|
|
20
|
+
const version = "0.0.8";
|
|
20
21
|
const packageJson = {
|
|
21
22
|
version: version};
|
|
22
23
|
|
|
@@ -146,6 +147,8 @@ function getSourceLabel(sourceKind) {
|
|
|
146
147
|
return "Installed package skill";
|
|
147
148
|
case "github":
|
|
148
149
|
return "Resolved module skill";
|
|
150
|
+
case "wellKnown":
|
|
151
|
+
return "Docs-discovered skill";
|
|
149
152
|
case "generated":
|
|
150
153
|
return "Metadata-routed skill";
|
|
151
154
|
}
|
|
@@ -206,8 +209,9 @@ function relativeModuleLink(entry, prefix) {
|
|
|
206
209
|
function groupModuleEntries(entries) {
|
|
207
210
|
const officialUpstream = entries.filter((entry) => entry.sourceKind === "dist");
|
|
208
211
|
const githubResolved = entries.filter((entry) => entry.sourceKind === "github");
|
|
212
|
+
const wellKnownResolved = entries.filter((entry) => entry.sourceKind === "wellKnown");
|
|
209
213
|
const metadataRouted = entries.filter((entry) => entry.sourceKind === "generated");
|
|
210
|
-
return { officialUpstream, githubResolved, metadataRouted };
|
|
214
|
+
return { officialUpstream, githubResolved, wellKnownResolved, metadataRouted };
|
|
211
215
|
}
|
|
212
216
|
function hasModuleGuidance(entries, skipped = []) {
|
|
213
217
|
return entries.length > 0 || skipped.length > 0;
|
|
@@ -330,6 +334,7 @@ function createSkillMapSections(metadata, entries, skipped = [], includeModuleAu
|
|
|
330
334
|
const moduleSections = [
|
|
331
335
|
renderModuleGroup("Official upstream skills", grouped.officialUpstream, "./references/modules/"),
|
|
332
336
|
renderModuleGroup("Resolved module skills", grouped.githubResolved, "./references/modules/"),
|
|
337
|
+
renderModuleGroup("Docs-discovered skills", grouped.wellKnownResolved, "./references/modules/"),
|
|
333
338
|
renderModuleGroup("Metadata-routed skills", grouped.metadataRouted, "./references/modules/"),
|
|
334
339
|
renderSkippedEntries(skipped)
|
|
335
340
|
].filter(Boolean);
|
|
@@ -494,24 +499,15 @@ function resolveSkillsDir(target, config) {
|
|
|
494
499
|
}
|
|
495
500
|
return void 0;
|
|
496
501
|
}
|
|
497
|
-
function normalizeTarget(target) {
|
|
498
|
-
return target.trim();
|
|
499
|
-
}
|
|
500
|
-
function getRawTargetConfig(target) {
|
|
501
|
-
return getAgentConfig(target);
|
|
502
|
-
}
|
|
503
502
|
function getSupportedTargets() {
|
|
504
|
-
return getAgentIds().filter((target) =>
|
|
505
|
-
const config = getRawTargetConfig(target);
|
|
506
|
-
return Boolean(resolveSkillsDir(target, config));
|
|
507
|
-
}).sort((a, b) => a.localeCompare(b));
|
|
503
|
+
return getAgentIds().filter((target) => Boolean(resolveSkillsDir(target, getAgentConfig(target)))).sort((a, b) => a.localeCompare(b));
|
|
508
504
|
}
|
|
509
505
|
function resolveAgentTargetConfig(target) {
|
|
510
|
-
const normalized =
|
|
506
|
+
const normalized = target.trim();
|
|
511
507
|
if (!normalized) {
|
|
512
508
|
return void 0;
|
|
513
509
|
}
|
|
514
|
-
const config =
|
|
510
|
+
const config = getAgentConfig(normalized);
|
|
515
511
|
const skillsDir = resolveSkillsDir(normalized, config);
|
|
516
512
|
if (!config || !skillsDir) {
|
|
517
513
|
return void 0;
|
|
@@ -523,11 +519,11 @@ function resolveAgentTargetConfig(target) {
|
|
|
523
519
|
};
|
|
524
520
|
}
|
|
525
521
|
function validateTargets(targets) {
|
|
526
|
-
const uniqueTargets = Array.from(new Set(targets.map(
|
|
522
|
+
const uniqueTargets = Array.from(new Set(targets.map((target) => target.trim()).filter(Boolean)));
|
|
527
523
|
const valid = [];
|
|
528
524
|
const invalid = [];
|
|
529
525
|
for (const target of uniqueTargets) {
|
|
530
|
-
const config =
|
|
526
|
+
const config = getAgentConfig(target);
|
|
531
527
|
if (!config) {
|
|
532
528
|
invalid.push({
|
|
533
529
|
target,
|
|
@@ -562,6 +558,24 @@ const MANAGED_HINT_START = "<!-- nuxt-skill-hub:start -->";
|
|
|
562
558
|
const MANAGED_HINT_END = "<!-- nuxt-skill-hub:end -->";
|
|
563
559
|
const SKILL_NAME_MAX_LENGTH = 64;
|
|
564
560
|
const SKILL_NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
561
|
+
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
562
|
+
if (!items.length) {
|
|
563
|
+
return [];
|
|
564
|
+
}
|
|
565
|
+
const results = [];
|
|
566
|
+
let nextIndex = 0;
|
|
567
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
568
|
+
while (true) {
|
|
569
|
+
const index = nextIndex++;
|
|
570
|
+
if (index >= items.length) {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
results[index] = await mapper(items[index], index);
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
await Promise.all(workers);
|
|
577
|
+
return results;
|
|
578
|
+
}
|
|
565
579
|
async function pathExists(path) {
|
|
566
580
|
try {
|
|
567
581
|
await promises.access(path);
|
|
@@ -746,6 +760,22 @@ async function discoverInstalledPackageFromDirectory(directory) {
|
|
|
746
760
|
}
|
|
747
761
|
return readInstalledPackageMetadata(packageJsonPath);
|
|
748
762
|
}
|
|
763
|
+
async function collectInstalledModulePackages(modules, rootDir) {
|
|
764
|
+
const specifiers = Array.from(new Set(
|
|
765
|
+
(modules || []).map((entry) => extractModuleSpecifier(entry)).filter((entry) => Boolean(entry))
|
|
766
|
+
));
|
|
767
|
+
const resolved = await Promise.all(
|
|
768
|
+
specifiers.map((specifier) => discoverInstalledPackageFromSpecifier(specifier, rootDir))
|
|
769
|
+
);
|
|
770
|
+
const seenPackageRoots = /* @__PURE__ */ new Set();
|
|
771
|
+
const packages = [];
|
|
772
|
+
for (const pkg of resolved) {
|
|
773
|
+
if (!pkg || seenPackageRoots.has(pkg.packageRoot)) continue;
|
|
774
|
+
seenPackageRoots.add(pkg.packageRoot);
|
|
775
|
+
packages.push(pkg);
|
|
776
|
+
}
|
|
777
|
+
return packages;
|
|
778
|
+
}
|
|
749
779
|
async function readInstalledPackageMetadata(packageJsonPath, fallbackSpecifier) {
|
|
750
780
|
const pkg = await readPackageJSON(packageJsonPath);
|
|
751
781
|
return {
|
|
@@ -863,15 +893,15 @@ async function validateContribution(contribution) {
|
|
|
863
893
|
};
|
|
864
894
|
}
|
|
865
895
|
async function validateResolvedContributions(contributions) {
|
|
896
|
+
const validations = await Promise.all(contributions.map(validateContribution));
|
|
866
897
|
const valid = [];
|
|
867
898
|
const issues = [];
|
|
868
|
-
for (const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
valid.push(validation.contribution);
|
|
899
|
+
for (const validation of validations) {
|
|
900
|
+
if (validation.issues.length) {
|
|
901
|
+
issues.push(...validation.issues);
|
|
872
902
|
continue;
|
|
873
903
|
}
|
|
874
|
-
|
|
904
|
+
valid.push(validation.contribution);
|
|
875
905
|
}
|
|
876
906
|
return {
|
|
877
907
|
contributions: sortAndDedupeContributions(valid),
|
|
@@ -1040,7 +1070,7 @@ function formatConflictWarning(conflict) {
|
|
|
1040
1070
|
return `Found ${conflict.skillNames.length} standalone skill(s) that may conflict with nuxt-skill-hub: ${conflict.skillNames.join(", ")} (global: ${conflict.globalDir}). Remove with: npx skills remove --global`;
|
|
1041
1071
|
}
|
|
1042
1072
|
|
|
1043
|
-
const
|
|
1073
|
+
const PACKAGE_OVERRIDES = [
|
|
1044
1074
|
{
|
|
1045
1075
|
packageName: "@nuxt/ui",
|
|
1046
1076
|
repo: "nuxt/ui",
|
|
@@ -1054,10 +1084,15 @@ const GITHUB_OVERRIDES = [
|
|
|
1054
1084
|
ref: "main",
|
|
1055
1085
|
path: "skills/vueuse-functions",
|
|
1056
1086
|
skillName: "vueuse-functions"
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
packageName: "docus",
|
|
1090
|
+
repo: "nuxt-content/docus",
|
|
1091
|
+
docsUrls: ["https://docus.dev/"]
|
|
1057
1092
|
}
|
|
1058
1093
|
];
|
|
1059
|
-
function
|
|
1060
|
-
return
|
|
1094
|
+
function findPackageOverride(packageName) {
|
|
1095
|
+
return PACKAGE_OVERRIDES.find((entry) => entry.packageName === packageName);
|
|
1061
1096
|
}
|
|
1062
1097
|
|
|
1063
1098
|
const GITHUB_API_BASE = "https://api.github.com";
|
|
@@ -1065,6 +1100,8 @@ const UNGH_API_BASE = "https://ungh.cc";
|
|
|
1065
1100
|
const githubDirectoryCache = /* @__PURE__ */ new Map();
|
|
1066
1101
|
const githubDefaultBranchCache = /* @__PURE__ */ new Map();
|
|
1067
1102
|
const githubFileTextCache = /* @__PURE__ */ new Map();
|
|
1103
|
+
const urlTextCache = /* @__PURE__ */ new Map();
|
|
1104
|
+
const urlBytesCache = /* @__PURE__ */ new Map();
|
|
1068
1105
|
function githubFetchOptions(timeoutMs) {
|
|
1069
1106
|
return {
|
|
1070
1107
|
timeout: timeoutMs,
|
|
@@ -1074,6 +1111,9 @@ function githubFetchOptions(timeoutMs) {
|
|
|
1074
1111
|
}
|
|
1075
1112
|
};
|
|
1076
1113
|
}
|
|
1114
|
+
function fetchStatus(error) {
|
|
1115
|
+
return typeof error === "object" && error && "status" in error ? Number(error.status) : void 0;
|
|
1116
|
+
}
|
|
1077
1117
|
function encodeGitHubPath(path) {
|
|
1078
1118
|
return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
|
|
1079
1119
|
}
|
|
@@ -1180,16 +1220,378 @@ async function fetchGitHubFileText(repo, ref, filePath, timeoutMs) {
|
|
|
1180
1220
|
);
|
|
1181
1221
|
return { ok: true, data };
|
|
1182
1222
|
} catch (error) {
|
|
1183
|
-
const status = typeof error === "object" && error && "status" in error ? Number(error.status) : void 0;
|
|
1184
1223
|
return {
|
|
1185
1224
|
ok: false,
|
|
1186
|
-
status,
|
|
1225
|
+
status: fetchStatus(error),
|
|
1226
|
+
error: error.message
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
async function fetchUrlText(url, timeoutMs) {
|
|
1232
|
+
return await memoize(urlTextCache, url, async () => {
|
|
1233
|
+
try {
|
|
1234
|
+
const data = await ofetch(url, {
|
|
1235
|
+
...githubFetchOptions(timeoutMs),
|
|
1236
|
+
responseType: "text"
|
|
1237
|
+
});
|
|
1238
|
+
return { ok: true, data };
|
|
1239
|
+
} catch (error) {
|
|
1240
|
+
return {
|
|
1241
|
+
ok: false,
|
|
1242
|
+
status: fetchStatus(error),
|
|
1243
|
+
error: error.message
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
async function fetchUrlJson(url, timeoutMs) {
|
|
1249
|
+
const text = await fetchUrlText(url, timeoutMs);
|
|
1250
|
+
if (!text.ok) {
|
|
1251
|
+
return {
|
|
1252
|
+
ok: false,
|
|
1253
|
+
status: text.status,
|
|
1254
|
+
error: text.error
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
try {
|
|
1258
|
+
return {
|
|
1259
|
+
ok: true,
|
|
1260
|
+
data: JSON.parse(text.data || "")
|
|
1261
|
+
};
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
return {
|
|
1264
|
+
ok: false,
|
|
1265
|
+
error: error.message
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
async function fetchUrlBytes(url, timeoutMs) {
|
|
1270
|
+
return await memoize(urlBytesCache, url, async () => {
|
|
1271
|
+
try {
|
|
1272
|
+
const data = await ofetch(url, {
|
|
1273
|
+
...githubFetchOptions(timeoutMs),
|
|
1274
|
+
responseType: "arrayBuffer"
|
|
1275
|
+
});
|
|
1276
|
+
return {
|
|
1277
|
+
ok: true,
|
|
1278
|
+
data: new Uint8Array(data)
|
|
1279
|
+
};
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
return {
|
|
1282
|
+
ok: false,
|
|
1283
|
+
status: fetchStatus(error),
|
|
1187
1284
|
error: error.message
|
|
1188
1285
|
};
|
|
1189
1286
|
}
|
|
1190
1287
|
});
|
|
1191
1288
|
}
|
|
1192
1289
|
|
|
1290
|
+
const WELL_KNOWN_DISCOVERY_V2_SCHEMA = "https://schemas.agentskills.io/discovery/0.2.0/schema.json";
|
|
1291
|
+
const WELL_KNOWN_DISCOVERY_V2_INDEX_PATH = "/.well-known/agent-skills/index.json";
|
|
1292
|
+
function makeSkip$1(packageName, skillName, reason) {
|
|
1293
|
+
return {
|
|
1294
|
+
packageName,
|
|
1295
|
+
skillName,
|
|
1296
|
+
reason,
|
|
1297
|
+
sourceKind: "wellKnown"
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
function normalizeHttpUrl$1(url) {
|
|
1301
|
+
const trimmed = url?.trim();
|
|
1302
|
+
if (!trimmed) {
|
|
1303
|
+
return void 0;
|
|
1304
|
+
}
|
|
1305
|
+
try {
|
|
1306
|
+
const parsed = new URL(trimmed);
|
|
1307
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:" ? parsed.toString() : void 0;
|
|
1308
|
+
} catch {
|
|
1309
|
+
return void 0;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
function normalizeHostname(hostname) {
|
|
1313
|
+
return hostname.trim().toLowerCase().replace(/^\[|\]$/g, "");
|
|
1314
|
+
}
|
|
1315
|
+
function parseIpv4(hostname) {
|
|
1316
|
+
const parts = hostname.split(".");
|
|
1317
|
+
if (parts.length !== 4) {
|
|
1318
|
+
return null;
|
|
1319
|
+
}
|
|
1320
|
+
const octets = parts.map((part) => {
|
|
1321
|
+
if (!/^\d+$/.test(part)) {
|
|
1322
|
+
return Number.NaN;
|
|
1323
|
+
}
|
|
1324
|
+
return Number(part);
|
|
1325
|
+
});
|
|
1326
|
+
if (octets.some((octet) => !Number.isInteger(octet) || octet < 0 || octet > 255)) {
|
|
1327
|
+
return null;
|
|
1328
|
+
}
|
|
1329
|
+
return octets;
|
|
1330
|
+
}
|
|
1331
|
+
function isPrivateIpv4(hostname) {
|
|
1332
|
+
const octets = parseIpv4(normalizeHostname(hostname));
|
|
1333
|
+
if (!octets) {
|
|
1334
|
+
return false;
|
|
1335
|
+
}
|
|
1336
|
+
const [a, b] = octets;
|
|
1337
|
+
return a === 0 || a === 10 || a === 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a >= 224 && a <= 239 || a >= 240;
|
|
1338
|
+
}
|
|
1339
|
+
function isPrivateIpv6(hostname) {
|
|
1340
|
+
const normalized = normalizeHostname(hostname);
|
|
1341
|
+
return normalized === "::" || normalized === "::1" || normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe8") || normalized.startsWith("fe9") || normalized.startsWith("fea") || normalized.startsWith("feb");
|
|
1342
|
+
}
|
|
1343
|
+
function isUnsafeHostname(hostname) {
|
|
1344
|
+
const normalized = normalizeHostname(hostname);
|
|
1345
|
+
if (normalized === "localhost" || normalized.endsWith(".localhost")) {
|
|
1346
|
+
return true;
|
|
1347
|
+
}
|
|
1348
|
+
const ipVersion = isIP(normalized);
|
|
1349
|
+
if (ipVersion === 4) {
|
|
1350
|
+
return isPrivateIpv4(normalized);
|
|
1351
|
+
}
|
|
1352
|
+
if (ipVersion === 6) {
|
|
1353
|
+
return isPrivateIpv6(normalized);
|
|
1354
|
+
}
|
|
1355
|
+
return false;
|
|
1356
|
+
}
|
|
1357
|
+
function isPublicDiscoveryBase(url) {
|
|
1358
|
+
try {
|
|
1359
|
+
const parsed = new URL(url);
|
|
1360
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.hostname === "github.com" || parseGitHubRepo(url) || isUnsafeHostname(parsed.hostname)) {
|
|
1361
|
+
return false;
|
|
1362
|
+
}
|
|
1363
|
+
return true;
|
|
1364
|
+
} catch {
|
|
1365
|
+
return false;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
function isSameOriginUrl(url, baseUrl) {
|
|
1369
|
+
return new URL(url).origin === new URL(baseUrl).origin;
|
|
1370
|
+
}
|
|
1371
|
+
function resolveSameOriginUrl(rawUrl, baseUrl) {
|
|
1372
|
+
const trimmed = rawUrl.trim();
|
|
1373
|
+
if (!trimmed) {
|
|
1374
|
+
return null;
|
|
1375
|
+
}
|
|
1376
|
+
try {
|
|
1377
|
+
const resolved = new URL(trimmed, baseUrl);
|
|
1378
|
+
if (!isSameOriginUrl(resolved.toString(), baseUrl) || isUnsafeHostname(resolved.hostname)) {
|
|
1379
|
+
return null;
|
|
1380
|
+
}
|
|
1381
|
+
return resolved.toString();
|
|
1382
|
+
} catch {
|
|
1383
|
+
return null;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
function docsBaseCandidates(packageInfo) {
|
|
1387
|
+
const override = findPackageOverride(packageInfo.packageName);
|
|
1388
|
+
const candidates = [
|
|
1389
|
+
...override?.docsUrls || [],
|
|
1390
|
+
packageInfo.homepage || ""
|
|
1391
|
+
];
|
|
1392
|
+
return [...new Set(candidates.map(normalizeHttpUrl$1).filter((url) => Boolean(url)).filter(isPublicDiscoveryBase))];
|
|
1393
|
+
}
|
|
1394
|
+
function normalizeDigest(value) {
|
|
1395
|
+
if (typeof value !== "string") {
|
|
1396
|
+
return null;
|
|
1397
|
+
}
|
|
1398
|
+
const trimmed = value.trim();
|
|
1399
|
+
return /^sha256:[a-f0-9]{64}$/.test(trimmed) ? trimmed : null;
|
|
1400
|
+
}
|
|
1401
|
+
function sha256(bytes) {
|
|
1402
|
+
return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
|
|
1403
|
+
}
|
|
1404
|
+
async function writeSkillFile(root, relativePath, bytes) {
|
|
1405
|
+
const destination = join(root, relativePath);
|
|
1406
|
+
await ensureDir(dirname(destination));
|
|
1407
|
+
await promises.writeFile(destination, bytes);
|
|
1408
|
+
}
|
|
1409
|
+
function contributionFor(input) {
|
|
1410
|
+
const sourceRepo = parseGitHubRepo(input.packageInfo.repository);
|
|
1411
|
+
return normalizeContribution({
|
|
1412
|
+
packageName: input.packageInfo.packageName,
|
|
1413
|
+
version: input.packageInfo.version,
|
|
1414
|
+
skillName: input.skillName,
|
|
1415
|
+
description: input.description,
|
|
1416
|
+
sourceKind: "wellKnown",
|
|
1417
|
+
sourceRepo: sourceRepo || void 0,
|
|
1418
|
+
sourcePath: input.indexUrl,
|
|
1419
|
+
repoUrl: sourceRepo ? `https://github.com/${sourceRepo}` : void 0,
|
|
1420
|
+
docsUrl: input.docsUrl,
|
|
1421
|
+
official: true,
|
|
1422
|
+
resolver: input.resolver,
|
|
1423
|
+
forceIncludeScripts: true
|
|
1424
|
+
}, input.targetDir, join(input.targetDir, ".."));
|
|
1425
|
+
}
|
|
1426
|
+
async function materializeV2Skill(input) {
|
|
1427
|
+
const skillName = typeof input.entry.name === "string" ? input.entry.name.trim() : "";
|
|
1428
|
+
if (!isValidSkillName(skillName)) {
|
|
1429
|
+
return { skipped: makeSkip$1(input.packageInfo.packageName, skillName || "entry", "well-known v2 skill name is invalid") };
|
|
1430
|
+
}
|
|
1431
|
+
const description = typeof input.entry.description === "string" ? input.entry.description.trim() : "";
|
|
1432
|
+
if (!description) {
|
|
1433
|
+
return { skipped: makeSkip$1(input.packageInfo.packageName, skillName, "well-known v2 skill is missing a description") };
|
|
1434
|
+
}
|
|
1435
|
+
const type = typeof input.entry.type === "string" ? input.entry.type.trim() : "";
|
|
1436
|
+
if (type === "archive") {
|
|
1437
|
+
return { skipped: makeSkip$1(input.packageInfo.packageName, skillName, "archive artifacts are not supported yet") };
|
|
1438
|
+
}
|
|
1439
|
+
if (type !== "skill-md") {
|
|
1440
|
+
return { skipped: makeSkip$1(input.packageInfo.packageName, skillName, `unsupported well-known v2 skill type "${type || "unknown"}"`) };
|
|
1441
|
+
}
|
|
1442
|
+
if (typeof input.entry.url !== "string" || !input.entry.url.trim()) {
|
|
1443
|
+
return { skipped: makeSkip$1(input.packageInfo.packageName, skillName, "well-known v2 skill is missing a URL") };
|
|
1444
|
+
}
|
|
1445
|
+
const digest = normalizeDigest(input.entry.digest);
|
|
1446
|
+
if (!digest) {
|
|
1447
|
+
return { skipped: makeSkip$1(input.packageInfo.packageName, skillName, "well-known v2 skill is missing a valid sha256 digest") };
|
|
1448
|
+
}
|
|
1449
|
+
const skillUrl = resolveSameOriginUrl(input.entry.url, input.indexUrl);
|
|
1450
|
+
if (!skillUrl) {
|
|
1451
|
+
return { skipped: makeSkip$1(input.packageInfo.packageName, skillName, "well-known v2 skill URL must stay on the discovery origin") };
|
|
1452
|
+
}
|
|
1453
|
+
const response = await fetchUrlBytes(skillUrl, input.timeoutMs);
|
|
1454
|
+
if (!response.ok || !response.data) {
|
|
1455
|
+
return { skipped: makeSkip$1(input.packageInfo.packageName, skillName, "failed to fetch well-known v2 skill artifact") };
|
|
1456
|
+
}
|
|
1457
|
+
if (sha256(response.data) !== digest) {
|
|
1458
|
+
return { skipped: makeSkip$1(input.packageInfo.packageName, skillName, "well-known v2 skill digest mismatch") };
|
|
1459
|
+
}
|
|
1460
|
+
const origin = new URL(input.docsUrl).origin;
|
|
1461
|
+
const targetDir = join(
|
|
1462
|
+
input.cacheRoot,
|
|
1463
|
+
"well-known",
|
|
1464
|
+
"rfc",
|
|
1465
|
+
sanitizeSegment(origin),
|
|
1466
|
+
sanitizeSegment(input.packageInfo.packageName),
|
|
1467
|
+
sanitizeSegment(input.packageInfo.version || "unknown"),
|
|
1468
|
+
sanitizeSegment(skillName)
|
|
1469
|
+
);
|
|
1470
|
+
await emptyDir(targetDir);
|
|
1471
|
+
await writeSkillFile(targetDir, "SKILL.md", response.data);
|
|
1472
|
+
return {
|
|
1473
|
+
contribution: contributionFor({
|
|
1474
|
+
packageInfo: input.packageInfo,
|
|
1475
|
+
targetDir,
|
|
1476
|
+
skillName,
|
|
1477
|
+
description,
|
|
1478
|
+
docsUrl: input.docsUrl,
|
|
1479
|
+
resolver: "wellKnownV2",
|
|
1480
|
+
indexUrl: input.indexUrl
|
|
1481
|
+
})
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
async function resolveV2Index(input) {
|
|
1485
|
+
const skipped = [];
|
|
1486
|
+
const contributions = [];
|
|
1487
|
+
if (!Array.isArray(input.index.skills)) {
|
|
1488
|
+
return {
|
|
1489
|
+
contributions: [],
|
|
1490
|
+
issues: [],
|
|
1491
|
+
skipped: [makeSkip$1(input.packageInfo.packageName, input.packageInfo.packageName, "well-known v2 index is missing a skills array")]
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
for (const rawEntry of input.index.skills) {
|
|
1495
|
+
if (!rawEntry || typeof rawEntry !== "object") {
|
|
1496
|
+
skipped.push(makeSkip$1(input.packageInfo.packageName, "entry", "well-known v2 skill entry must be an object"));
|
|
1497
|
+
continue;
|
|
1498
|
+
}
|
|
1499
|
+
const materialized = await materializeV2Skill({
|
|
1500
|
+
packageInfo: input.packageInfo,
|
|
1501
|
+
entry: rawEntry,
|
|
1502
|
+
indexUrl: input.indexUrl,
|
|
1503
|
+
docsUrl: input.docsUrl,
|
|
1504
|
+
cacheRoot: input.cacheRoot,
|
|
1505
|
+
timeoutMs: input.timeoutMs
|
|
1506
|
+
});
|
|
1507
|
+
if (materialized?.contribution) {
|
|
1508
|
+
contributions.push(materialized.contribution);
|
|
1509
|
+
}
|
|
1510
|
+
if (materialized?.skipped) {
|
|
1511
|
+
skipped.push(materialized.skipped);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
return { contributions, issues: [], skipped };
|
|
1515
|
+
}
|
|
1516
|
+
async function resolveIndex(input) {
|
|
1517
|
+
const response = await fetchUrlJson(input.indexUrl, input.timeoutMs);
|
|
1518
|
+
if (!response.ok) {
|
|
1519
|
+
if (response.status === 404) {
|
|
1520
|
+
return null;
|
|
1521
|
+
}
|
|
1522
|
+
return {
|
|
1523
|
+
contributions: [],
|
|
1524
|
+
issues: [],
|
|
1525
|
+
skipped: [makeSkip$1(input.packageInfo.packageName, input.packageInfo.packageName, `failed to fetch well-known index ${input.indexUrl}`)]
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
const index = response.data;
|
|
1529
|
+
if (!index || typeof index !== "object") {
|
|
1530
|
+
return {
|
|
1531
|
+
contributions: [],
|
|
1532
|
+
issues: [],
|
|
1533
|
+
skipped: [makeSkip$1(input.packageInfo.packageName, input.packageInfo.packageName, "well-known index must be an object")]
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
if (index.$schema === WELL_KNOWN_DISCOVERY_V2_SCHEMA) {
|
|
1537
|
+
return await resolveV2Index({
|
|
1538
|
+
...input,
|
|
1539
|
+
index
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
if (typeof index.$schema === "string" && index.$schema) {
|
|
1543
|
+
return {
|
|
1544
|
+
contributions: [],
|
|
1545
|
+
issues: [],
|
|
1546
|
+
skipped: [makeSkip$1(input.packageInfo.packageName, input.packageInfo.packageName, `unsupported well-known schema "${index.$schema}"`)]
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
return {
|
|
1550
|
+
contributions: [],
|
|
1551
|
+
issues: [],
|
|
1552
|
+
skipped: [makeSkip$1(input.packageInfo.packageName, input.packageInfo.packageName, "well-known index is missing the v2 schema")]
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
async function resolveViaWellKnown(packageInfo, cacheRoot, timeoutMs) {
|
|
1556
|
+
const bases = docsBaseCandidates(packageInfo);
|
|
1557
|
+
const skipped = [];
|
|
1558
|
+
const issues = [];
|
|
1559
|
+
for (const docsUrl of bases) {
|
|
1560
|
+
const indexUrl = new URL(WELL_KNOWN_DISCOVERY_V2_INDEX_PATH, docsUrl).toString();
|
|
1561
|
+
const result = await resolveIndex({
|
|
1562
|
+
packageInfo,
|
|
1563
|
+
indexUrl,
|
|
1564
|
+
docsUrl,
|
|
1565
|
+
cacheRoot,
|
|
1566
|
+
timeoutMs
|
|
1567
|
+
});
|
|
1568
|
+
if (!result) {
|
|
1569
|
+
continue;
|
|
1570
|
+
}
|
|
1571
|
+
issues.push(...result.issues);
|
|
1572
|
+
skipped.push(...result.skipped);
|
|
1573
|
+
if (result.contributions.length) {
|
|
1574
|
+
return {
|
|
1575
|
+
contributions: result.contributions,
|
|
1576
|
+
issues,
|
|
1577
|
+
skipped
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
if (!bases.length) {
|
|
1582
|
+
return {
|
|
1583
|
+
contributions: [],
|
|
1584
|
+
issues: [],
|
|
1585
|
+
skipped: []
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
return {
|
|
1589
|
+
contributions: [],
|
|
1590
|
+
issues,
|
|
1591
|
+
skipped
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1193
1595
|
function makeSkip(packageName, skillName, reason, sourceKind) {
|
|
1194
1596
|
return {
|
|
1195
1597
|
packageName,
|
|
@@ -1231,8 +1633,14 @@ function normalizeHttpUrl(url) {
|
|
|
1231
1633
|
return void 0;
|
|
1232
1634
|
}
|
|
1233
1635
|
}
|
|
1636
|
+
function resolveDocsUrl(packageInfo) {
|
|
1637
|
+
const override = findPackageOverride(packageInfo.packageName);
|
|
1638
|
+
const overrideDocsUrl = override?.docsUrls?.map(normalizeHttpUrl).find(Boolean);
|
|
1639
|
+
return overrideDocsUrl || normalizeHttpUrl(packageInfo.homepage);
|
|
1640
|
+
}
|
|
1234
1641
|
function resolveRepositoryUrl(packageInfo) {
|
|
1235
|
-
const
|
|
1642
|
+
const override = findPackageOverride(packageInfo.packageName);
|
|
1643
|
+
const sourceRepo = override?.repo || parseGitHubRepo(packageInfo.repository) || parseGitHubRepo(packageInfo.homepage);
|
|
1236
1644
|
if (sourceRepo) {
|
|
1237
1645
|
return {
|
|
1238
1646
|
sourceRepo,
|
|
@@ -1304,18 +1712,9 @@ async function materializeCandidate(packageInfo, candidate, cacheRoot) {
|
|
|
1304
1712
|
forceIncludeScripts: true
|
|
1305
1713
|
}, targetDir, join(targetDir, ".."));
|
|
1306
1714
|
}
|
|
1307
|
-
|
|
1308
|
-
const
|
|
1309
|
-
const skipped = [];
|
|
1310
|
-
const override = findGitHubOverride(packageInfo.packageName);
|
|
1715
|
+
function createGitHubResolveContext(packageInfo) {
|
|
1716
|
+
const override = findPackageOverride(packageInfo.packageName);
|
|
1311
1717
|
const repo = override?.repo || parseGitHubRepo(packageInfo.repository) || parseGitHubRepo(packageInfo.homepage);
|
|
1312
|
-
if (!repo) {
|
|
1313
|
-
return {
|
|
1314
|
-
contributions: [],
|
|
1315
|
-
issues,
|
|
1316
|
-
skipped: [makeSkip(packageInfo.packageName, override?.skillName || packageInfo.packageName, "No GitHub repository metadata found", "github")]
|
|
1317
|
-
};
|
|
1318
|
-
}
|
|
1319
1718
|
const refs = dedupe([
|
|
1320
1719
|
override?.ref || "",
|
|
1321
1720
|
packageInfo.version ? `v${packageInfo.version}` : "",
|
|
@@ -1335,35 +1734,89 @@ async function resolveViaGitHub(packageInfo, cacheRoot, timeoutMs) {
|
|
|
1335
1734
|
];
|
|
1336
1735
|
})
|
|
1337
1736
|
]);
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1737
|
+
return {
|
|
1738
|
+
issues: [],
|
|
1739
|
+
skipped: [],
|
|
1740
|
+
override,
|
|
1741
|
+
repo: repo || void 0,
|
|
1742
|
+
refs,
|
|
1743
|
+
pathCandidates
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
async function resolveAgentSkillsForRef(packageInfo, context, ref, cacheRoot, timeoutMs) {
|
|
1747
|
+
if (!context.repo) {
|
|
1748
|
+
return null;
|
|
1749
|
+
}
|
|
1750
|
+
const packageJson = await fetchGitHubFileText(context.repo, ref, "package.json", timeoutMs);
|
|
1751
|
+
if (!packageJson.ok || !packageJson.data) {
|
|
1752
|
+
return null;
|
|
1753
|
+
}
|
|
1754
|
+
try {
|
|
1755
|
+
const parsed = JSON.parse(packageJson.data);
|
|
1756
|
+
const remoteSkills = parseAgentSkillDeclarations(parsed, packageInfo.packageName, "github");
|
|
1757
|
+
context.issues.push(...remoteSkills.issues);
|
|
1758
|
+
for (const skill of remoteSkills.skills) {
|
|
1759
|
+
const resolved = await materializeCandidate(packageInfo, {
|
|
1760
|
+
skillName: skill.name,
|
|
1761
|
+
sourcePath: skill.path,
|
|
1762
|
+
sourceKind: "github",
|
|
1763
|
+
sourceRepo: context.repo,
|
|
1764
|
+
sourceRef: ref,
|
|
1765
|
+
official: true,
|
|
1766
|
+
resolver: "agentsField"
|
|
1767
|
+
}, cacheRoot);
|
|
1768
|
+
if (resolved) {
|
|
1769
|
+
return [resolved];
|
|
1361
1770
|
}
|
|
1362
1771
|
}
|
|
1363
|
-
|
|
1364
|
-
|
|
1772
|
+
} catch {
|
|
1773
|
+
context.issues.push(createValidationIssue(packageInfo.packageName, packageInfo.packageName, "Failed to parse remote package.json", "github"));
|
|
1774
|
+
}
|
|
1775
|
+
return null;
|
|
1776
|
+
}
|
|
1777
|
+
async function resolveViaGitHubAgents(packageInfo, cacheRoot, timeoutMs) {
|
|
1778
|
+
const context = createGitHubResolveContext(packageInfo);
|
|
1779
|
+
if (!context.repo) {
|
|
1780
|
+
return {
|
|
1781
|
+
contributions: [],
|
|
1782
|
+
issues: context.issues,
|
|
1783
|
+
skipped: []
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
for (const ref of context.refs) {
|
|
1787
|
+
const resolved = await resolveAgentSkillsForRef(packageInfo, context, ref, cacheRoot, timeoutMs);
|
|
1788
|
+
if (resolved?.length) {
|
|
1789
|
+
return { contributions: resolved, issues: context.issues, skipped: context.skipped };
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
const fallbackRefs = dedupe([
|
|
1793
|
+
await fetchGitHubDefaultBranch(context.repo, timeoutMs) || "",
|
|
1794
|
+
"main",
|
|
1795
|
+
"master"
|
|
1796
|
+
]);
|
|
1797
|
+
for (const ref of fallbackRefs) {
|
|
1798
|
+
const resolved = await resolveAgentSkillsForRef(packageInfo, context, ref, cacheRoot, timeoutMs);
|
|
1799
|
+
if (resolved?.length) {
|
|
1800
|
+
return { contributions: resolved, issues: context.issues, skipped: context.skipped };
|
|
1365
1801
|
}
|
|
1366
|
-
|
|
1802
|
+
}
|
|
1803
|
+
return {
|
|
1804
|
+
contributions: [],
|
|
1805
|
+
issues: context.issues,
|
|
1806
|
+
skipped: context.skipped
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
async function resolveViaGitHubHeuristics(packageInfo, cacheRoot, timeoutMs) {
|
|
1810
|
+
const context = createGitHubResolveContext(packageInfo);
|
|
1811
|
+
if (!context.repo) {
|
|
1812
|
+
return {
|
|
1813
|
+
contributions: [],
|
|
1814
|
+
issues: context.issues,
|
|
1815
|
+
skipped: [makeSkip(packageInfo.packageName, context.override?.skillName || packageInfo.packageName, "No GitHub repository metadata found", "github")]
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
const tryResolveForRef = async (ref, allowTreeLookup) => {
|
|
1819
|
+
const skillsDirs = await listGitHubDirectory(context.repo, ref, "skills", timeoutMs) ;
|
|
1367
1820
|
if (skillsDirs.length) {
|
|
1368
1821
|
const contributions = [];
|
|
1369
1822
|
for (const skillName of skillsDirs) {
|
|
@@ -1371,7 +1824,7 @@ async function resolveViaGitHub(packageInfo, cacheRoot, timeoutMs) {
|
|
|
1371
1824
|
skillName,
|
|
1372
1825
|
sourcePath: `skills/${skillName}`,
|
|
1373
1826
|
sourceKind: "github",
|
|
1374
|
-
sourceRepo: repo,
|
|
1827
|
+
sourceRepo: context.repo,
|
|
1375
1828
|
sourceRef: ref,
|
|
1376
1829
|
official: true,
|
|
1377
1830
|
resolver: "githubHeuristic"
|
|
@@ -1384,13 +1837,13 @@ async function resolveViaGitHub(packageInfo, cacheRoot, timeoutMs) {
|
|
|
1384
1837
|
return contributions;
|
|
1385
1838
|
}
|
|
1386
1839
|
}
|
|
1387
|
-
for (const path of pathCandidates) {
|
|
1388
|
-
const candidateSkillName = override?.path && path === override.path ? override.skillName || path.split("/").pop() || packageInfo.packageName : path.split("/").pop() || packageInfo.packageName;
|
|
1840
|
+
for (const path of context.pathCandidates) {
|
|
1841
|
+
const candidateSkillName = context.override?.path && path === context.override.path ? context.override.skillName || path.split("/").pop() || packageInfo.packageName : path.split("/").pop() || packageInfo.packageName;
|
|
1389
1842
|
const resolved = await materializeCandidate(packageInfo, {
|
|
1390
1843
|
skillName: candidateSkillName,
|
|
1391
1844
|
sourcePath: path,
|
|
1392
1845
|
sourceKind: "github",
|
|
1393
|
-
sourceRepo: repo,
|
|
1846
|
+
sourceRepo: context.repo,
|
|
1394
1847
|
sourceRef: ref,
|
|
1395
1848
|
official: true,
|
|
1396
1849
|
resolver: "githubHeuristic"
|
|
@@ -1401,39 +1854,33 @@ async function resolveViaGitHub(packageInfo, cacheRoot, timeoutMs) {
|
|
|
1401
1854
|
}
|
|
1402
1855
|
return null;
|
|
1403
1856
|
};
|
|
1404
|
-
for (const ref of refs) {
|
|
1405
|
-
const resolved = await tryResolveForRef(ref, false);
|
|
1406
|
-
if (resolved?.length) {
|
|
1407
|
-
return { contributions: resolved, issues, skipped };
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
1857
|
const fallbackRefs = dedupe([
|
|
1411
|
-
await fetchGitHubDefaultBranch(repo, timeoutMs) || "",
|
|
1858
|
+
await fetchGitHubDefaultBranch(context.repo, timeoutMs) || "",
|
|
1412
1859
|
"main",
|
|
1413
1860
|
"master"
|
|
1414
1861
|
]);
|
|
1415
1862
|
for (const ref of fallbackRefs) {
|
|
1416
|
-
const resolved = await tryResolveForRef(ref
|
|
1863
|
+
const resolved = await tryResolveForRef(ref);
|
|
1417
1864
|
if (resolved?.length) {
|
|
1418
|
-
return { contributions: resolved, issues, skipped };
|
|
1865
|
+
return { contributions: resolved, issues: context.issues, skipped: context.skipped };
|
|
1419
1866
|
}
|
|
1420
1867
|
}
|
|
1421
|
-
for (const ref of refs) {
|
|
1422
|
-
const resolved = await tryResolveForRef(ref
|
|
1868
|
+
for (const ref of context.refs) {
|
|
1869
|
+
const resolved = await tryResolveForRef(ref);
|
|
1423
1870
|
if (resolved?.length) {
|
|
1424
|
-
return { contributions: resolved, issues, skipped };
|
|
1871
|
+
return { contributions: resolved, issues: context.issues, skipped: context.skipped };
|
|
1425
1872
|
}
|
|
1426
1873
|
}
|
|
1427
|
-
skipped.push(makeSkip(packageInfo.packageName, override?.skillName || packageInfo.packageName, "No GitHub skill found using configured refs and path heuristics", "github"));
|
|
1874
|
+
context.skipped.push(makeSkip(packageInfo.packageName, context.override?.skillName || packageInfo.packageName, "No GitHub skill found using configured refs and path heuristics", "github"));
|
|
1428
1875
|
return {
|
|
1429
1876
|
contributions: [],
|
|
1430
|
-
issues,
|
|
1431
|
-
skipped
|
|
1877
|
+
issues: context.issues,
|
|
1878
|
+
skipped: context.skipped
|
|
1432
1879
|
};
|
|
1433
1880
|
}
|
|
1434
1881
|
async function resolveViaMetadataRouter(packageInfo, cacheRoot) {
|
|
1435
1882
|
const { repoUrl, sourceRepo } = resolveRepositoryUrl(packageInfo);
|
|
1436
|
-
const docsUrl =
|
|
1883
|
+
const docsUrl = resolveDocsUrl(packageInfo);
|
|
1437
1884
|
if (!repoUrl && !docsUrl) {
|
|
1438
1885
|
return {
|
|
1439
1886
|
contributions: [],
|
|
@@ -1505,26 +1952,46 @@ async function resolveViaMetadataRouter(packageInfo, cacheRoot) {
|
|
|
1505
1952
|
};
|
|
1506
1953
|
}
|
|
1507
1954
|
async function resolveRemoteContributionsForPackage(packageInfo, options) {
|
|
1508
|
-
const
|
|
1955
|
+
const githubAgentsResult = options.enableGithubLookup ? await resolveViaGitHubAgents(packageInfo, options.cacheRoot, options.githubLookupTimeoutMs) : {
|
|
1509
1956
|
contributions: [],
|
|
1510
1957
|
issues: [],
|
|
1511
1958
|
skipped: []
|
|
1512
1959
|
};
|
|
1513
|
-
if (
|
|
1514
|
-
return
|
|
1960
|
+
if (githubAgentsResult.contributions.length) {
|
|
1961
|
+
return githubAgentsResult;
|
|
1962
|
+
}
|
|
1963
|
+
const wellKnownResult = await resolveViaWellKnown(packageInfo, options.cacheRoot, options.githubLookupTimeoutMs);
|
|
1964
|
+
if (wellKnownResult.contributions.length) {
|
|
1965
|
+
return {
|
|
1966
|
+
contributions: wellKnownResult.contributions,
|
|
1967
|
+
issues: [...githubAgentsResult.issues, ...wellKnownResult.issues],
|
|
1968
|
+
skipped: wellKnownResult.skipped
|
|
1969
|
+
};
|
|
1970
|
+
}
|
|
1971
|
+
const githubHeuristicResult = options.enableGithubLookup ? await resolveViaGitHubHeuristics(packageInfo, options.cacheRoot, options.githubLookupTimeoutMs) : {
|
|
1972
|
+
contributions: [],
|
|
1973
|
+
issues: [],
|
|
1974
|
+
skipped: []
|
|
1975
|
+
};
|
|
1976
|
+
if (githubHeuristicResult.contributions.length) {
|
|
1977
|
+
return {
|
|
1978
|
+
contributions: githubHeuristicResult.contributions,
|
|
1979
|
+
issues: [...githubAgentsResult.issues, ...wellKnownResult.issues, ...githubHeuristicResult.issues],
|
|
1980
|
+
skipped: wellKnownResult.skipped
|
|
1981
|
+
};
|
|
1515
1982
|
}
|
|
1516
1983
|
const generatedResult = await resolveViaMetadataRouter(packageInfo, options.cacheRoot);
|
|
1517
1984
|
if (generatedResult.contributions.length) {
|
|
1518
1985
|
return {
|
|
1519
1986
|
contributions: generatedResult.contributions,
|
|
1520
|
-
issues: [...
|
|
1521
|
-
skipped: generatedResult.skipped
|
|
1987
|
+
issues: [...githubAgentsResult.issues, ...wellKnownResult.issues, ...githubHeuristicResult.issues, ...generatedResult.issues],
|
|
1988
|
+
skipped: [...wellKnownResult.skipped, ...generatedResult.skipped]
|
|
1522
1989
|
};
|
|
1523
1990
|
}
|
|
1524
1991
|
return {
|
|
1525
1992
|
contributions: [],
|
|
1526
|
-
issues: [...
|
|
1527
|
-
skipped: [...
|
|
1993
|
+
issues: [...githubAgentsResult.issues, ...wellKnownResult.issues, ...githubHeuristicResult.issues, ...generatedResult.issues],
|
|
1994
|
+
skipped: [...wellKnownResult.skipped, ...githubHeuristicResult.skipped, ...generatedResult.skipped]
|
|
1528
1995
|
};
|
|
1529
1996
|
}
|
|
1530
1997
|
|
|
@@ -1582,6 +2049,7 @@ async function hashFileIfExists(path) {
|
|
|
1582
2049
|
}
|
|
1583
2050
|
}
|
|
1584
2051
|
|
|
2052
|
+
const HASH_READ_CONCURRENCY = 16;
|
|
1585
2053
|
async function resolveManualContribution(rootDir, contribution) {
|
|
1586
2054
|
const sourceDir = isAbsolute(contribution.sourceDir) ? contribution.sourceDir : resolve(rootDir, contribution.sourceDir);
|
|
1587
2055
|
return normalizeContribution(contribution, sourceDir, sourceDir);
|
|
@@ -1633,8 +2101,6 @@ async function hashDirectoryTreeIfExists(rootPath) {
|
|
|
1633
2101
|
if (!rootStat.isDirectory()) {
|
|
1634
2102
|
return null;
|
|
1635
2103
|
}
|
|
1636
|
-
const hash = createHash("sha256");
|
|
1637
|
-
hash.update("dir:.\n");
|
|
1638
2104
|
const entries = await glob("**/*", {
|
|
1639
2105
|
cwd: rootPath,
|
|
1640
2106
|
dot: true,
|
|
@@ -1643,30 +2109,20 @@ async function hashDirectoryTreeIfExists(rootPath) {
|
|
|
1643
2109
|
onlyFiles: false
|
|
1644
2110
|
});
|
|
1645
2111
|
entries.sort((a, b) => a.localeCompare(b));
|
|
1646
|
-
|
|
2112
|
+
const chunkGroups = await mapWithConcurrency(entries, HASH_READ_CONCURRENCY, async (entry) => {
|
|
1647
2113
|
const currentPath = join(rootPath, entry);
|
|
1648
2114
|
const stat = await promises.lstat(currentPath);
|
|
1649
|
-
const
|
|
1650
|
-
if (stat.isSymbolicLink()) {
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
}
|
|
1661
|
-
if (stat.isFile()) {
|
|
1662
|
-
hash.update(`file:${relativePath}:`);
|
|
1663
|
-
hash.update(await promises.readFile(currentPath));
|
|
1664
|
-
hash.update("\n");
|
|
1665
|
-
continue;
|
|
1666
|
-
}
|
|
1667
|
-
hash.update(`other:${relativePath}:${stat.mode}
|
|
1668
|
-
`);
|
|
1669
|
-
}
|
|
2115
|
+
const rel = relative(rootPath, currentPath).replaceAll("\\", "/");
|
|
2116
|
+
if (stat.isSymbolicLink()) return [`symlink:${rel}:`, await promises.readlink(currentPath), "\n"];
|
|
2117
|
+
if (stat.isDirectory()) return [`dir:${rel}
|
|
2118
|
+
`];
|
|
2119
|
+
if (stat.isFile()) return [`file:${rel}:`, await promises.readFile(currentPath), "\n"];
|
|
2120
|
+
return [`other:${rel}:${stat.mode}
|
|
2121
|
+
`];
|
|
2122
|
+
});
|
|
2123
|
+
const hash = createHash("sha256");
|
|
2124
|
+
hash.update("dir:.\n");
|
|
2125
|
+
for (const chunks of chunkGroups) for (const chunk of chunks) hash.update(chunk);
|
|
1670
2126
|
return hash.digest("hex");
|
|
1671
2127
|
}
|
|
1672
2128
|
|
|
@@ -1727,24 +2183,6 @@ function getPersistentCacheRoot(exportRoot) {
|
|
|
1727
2183
|
function getCachedGeneratedSkillRoot(cacheRoot, skillName) {
|
|
1728
2184
|
return join(cacheRoot, "generated", skillName);
|
|
1729
2185
|
}
|
|
1730
|
-
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
1731
|
-
if (!items.length) {
|
|
1732
|
-
return [];
|
|
1733
|
-
}
|
|
1734
|
-
const results = [];
|
|
1735
|
-
let nextIndex = 0;
|
|
1736
|
-
const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
1737
|
-
while (true) {
|
|
1738
|
-
const index = nextIndex++;
|
|
1739
|
-
if (index >= items.length) {
|
|
1740
|
-
return;
|
|
1741
|
-
}
|
|
1742
|
-
results[index] = await mapper(items[index], index);
|
|
1743
|
-
}
|
|
1744
|
-
});
|
|
1745
|
-
await Promise.all(workers);
|
|
1746
|
-
return results;
|
|
1747
|
-
}
|
|
1748
2186
|
function mergeSkipped(entries, keyFn) {
|
|
1749
2187
|
const byKey = /* @__PURE__ */ new Map();
|
|
1750
2188
|
for (const entry of entries) {
|
|
@@ -1781,17 +2219,13 @@ async function collectInstalledPackages(nuxt) {
|
|
|
1781
2219
|
discoveries.push(discovered);
|
|
1782
2220
|
}
|
|
1783
2221
|
};
|
|
1784
|
-
const
|
|
1785
|
-
const
|
|
1786
|
-
for (const specifier of seenSpecifiers) {
|
|
1787
|
-
addInstalledPackage(await discoverInstalledPackageFromSpecifier(specifier, nuxt.options.rootDir));
|
|
1788
|
-
}
|
|
2222
|
+
const modulePackages = await collectInstalledModulePackages(nuxt.options.modules, nuxt.options.rootDir);
|
|
2223
|
+
for (const pkg of modulePackages) addInstalledPackage(pkg);
|
|
1789
2224
|
const layerDirectories = Array.from(new Set(
|
|
1790
2225
|
nuxt.options._layers.map((layer) => resolve(layer.cwd || layer.config.rootDir || "")).filter((layerDir) => layerDir && layerDir !== resolve(nuxt.options.rootDir))
|
|
1791
2226
|
));
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
}
|
|
2227
|
+
const layerPackages = await Promise.all(layerDirectories.map((dir) => discoverInstalledPackageFromDirectory(dir)));
|
|
2228
|
+
for (const pkg of layerPackages) addInstalledPackage(pkg);
|
|
1795
2229
|
const localPackageSkills = await discoverLocalPackageSkills(nuxt.options.rootDir);
|
|
1796
2230
|
if (localPackageSkills) {
|
|
1797
2231
|
discoveries.push(localPackageSkills);
|
|
@@ -1971,23 +2405,14 @@ async function generateSkillTree(input) {
|
|
|
1971
2405
|
function isCancelled(value) {
|
|
1972
2406
|
return value === null || typeof value === "symbol";
|
|
1973
2407
|
}
|
|
1974
|
-
const AI_AGENT_TARGET_MAP = {
|
|
1975
|
-
codex: "codex",
|
|
1976
|
-
claude: "claude-code",
|
|
1977
|
-
gemini: "gemini-code-assist",
|
|
1978
|
-
replit: "replit-agent"
|
|
1979
|
-
};
|
|
1980
|
-
function resolveAIGuidanceTarget(currentTarget, detectedAgent) {
|
|
1981
|
-
if (currentTarget) {
|
|
1982
|
-
return currentTarget;
|
|
1983
|
-
}
|
|
1984
|
-
if (!detectedAgent) {
|
|
1985
|
-
return void 0;
|
|
1986
|
-
}
|
|
1987
|
-
return AI_AGENT_TARGET_MAP[detectedAgent];
|
|
1988
|
-
}
|
|
1989
2408
|
function buildAIGuidance(currentTarget, detectedAgent) {
|
|
1990
|
-
const
|
|
2409
|
+
const agentTargetMap = {
|
|
2410
|
+
codex: "codex",
|
|
2411
|
+
claude: "claude-code",
|
|
2412
|
+
gemini: "gemini-code-assist",
|
|
2413
|
+
replit: "replit-agent"
|
|
2414
|
+
};
|
|
2415
|
+
const target = currentTarget || (detectedAgent ? agentTargetMap[detectedAgent] : void 0);
|
|
1991
2416
|
const sourceLabel = detectedAgent ? ` (${detectedAgent})` : "";
|
|
1992
2417
|
const snippetLines = [
|
|
1993
2418
|
"export default defineNuxtConfig({",
|
|
@@ -2068,15 +2493,7 @@ Let's configure AI agent skills for your Nuxt project.`
|
|
|
2068
2493
|
}
|
|
2069
2494
|
}
|
|
2070
2495
|
}
|
|
2071
|
-
const
|
|
2072
|
-
const uniqueSpecifiers = Array.from(new Set(moduleSpecifiers));
|
|
2073
|
-
const installedModulePackages = [];
|
|
2074
|
-
for (const specifier of uniqueSpecifiers) {
|
|
2075
|
-
const pkg = await discoverInstalledPackageFromSpecifier(specifier, rootDir);
|
|
2076
|
-
if (!pkg || pkg.packageName === "nuxt-skill-hub")
|
|
2077
|
-
continue;
|
|
2078
|
-
installedModulePackages.push(pkg.packageName);
|
|
2079
|
-
}
|
|
2496
|
+
const installedModulePackages = (await collectInstalledModulePackages(nuxt.options.modules, rootDir)).map((pkg) => pkg.packageName).filter((name) => name !== "nuxt-skill-hub");
|
|
2080
2497
|
if (installedModulePackages.length) {
|
|
2081
2498
|
consola.info(`Detected ${installedModulePackages.length} installed module package(s):`);
|
|
2082
2499
|
for (const packageName of installedModulePackages) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-skill-hub",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Teach your AI agent the Nuxt way with best practices and module guidance.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -61,11 +61,13 @@
|
|
|
61
61
|
"dev:build": "nuxt-module-build build --stub --watch",
|
|
62
62
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare",
|
|
63
63
|
"release": "bumpp --push --no-push-all",
|
|
64
|
+
"release:check-version": "node ./scripts/check-npm-version.mjs",
|
|
64
65
|
"lint": "eslint .",
|
|
65
|
-
"test": "pnpm run dev:prepare && vitest run && pnpm run test:pack",
|
|
66
|
+
"test": "pnpm run dev:prepare && pnpm run docs:prepare && vitest run && pnpm run test:pack",
|
|
66
67
|
"test:pack": "node ./scripts/check-packlist.mjs",
|
|
67
68
|
"test:watch": "vitest watch",
|
|
68
69
|
"test:types": "pnpm run dev:prepare && vue-tsc --noEmit",
|
|
70
|
+
"docs:prepare": "pnpm --dir docs exec nuxt prepare",
|
|
69
71
|
"docs:typecheck": "pnpm --dir docs typecheck",
|
|
70
72
|
"typecheck": "pnpm run test:types && pnpm run docs:typecheck"
|
|
71
73
|
}
|