skilld 0.9.5 → 0.10.0

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.
@@ -1,9 +1,10 @@
1
1
  import { i as getCacheDir } from "./config.mjs";
2
- import { c as getBlogPreset, l as getDocOverride, n as yamlParseKV, o as mapInsert } from "./yaml.mjs";
2
+ import { a as getDocOverride, i as getBlogPreset, n as yamlParseKV } from "./yaml.mjs";
3
3
  import { basename, dirname, join, resolve } from "pathe";
4
4
  import { createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, unlinkSync } from "node:fs";
5
5
  import { htmlToMarkdown } from "mdream";
6
6
  import { spawnSync } from "node:child_process";
7
+ import { gt } from "semver";
7
8
  import { ofetch } from "ofetch";
8
9
  import { globby } from "globby";
9
10
  import pLimit from "p-limit";
@@ -24,6 +25,22 @@ function buildFrontmatter(fields) {
24
25
  lines.push("---");
25
26
  return lines.join("\n");
26
27
  }
28
+ function mapInsert(map, key, create) {
29
+ let val = map.get(key);
30
+ if (val === void 0) {
31
+ val = create();
32
+ map.set(key, val);
33
+ }
34
+ return val;
35
+ }
36
+ function semverGt(a, b) {
37
+ return gt(a, b, true);
38
+ }
39
+ const SHARED_SKILLS_DIR = ".skills";
40
+ function getSharedSkillsDir(cwd = process.cwd()) {
41
+ const dir = join(cwd, SHARED_SKILLS_DIR);
42
+ return existsSync(dir) ? dir : null;
43
+ }
27
44
  let _ghAvailable;
28
45
  function isGhAvailable() {
29
46
  if (_ghAvailable !== void 0) return _ghAvailable;
@@ -401,6 +418,25 @@ function parseGitHubUrl(url) {
401
418
  function normalizeRepoUrl(url) {
402
419
  return url.replace(/^git\+/, "").replace(/#.*$/, "").replace(/\.git$/, "").replace(/^git:\/\//, "https://").replace(/^ssh:\/\/git@github\.com/, "https://github.com").replace(/^git@github\.com:/, "https://github.com/");
403
420
  }
421
+ function parsePackageSpec(spec) {
422
+ if (spec.startsWith("@")) {
423
+ const slashIdx = spec.indexOf("/");
424
+ if (slashIdx !== -1) {
425
+ const atIdx = spec.indexOf("@", slashIdx + 1);
426
+ if (atIdx !== -1) return {
427
+ name: spec.slice(0, atIdx),
428
+ tag: spec.slice(atIdx + 1)
429
+ };
430
+ }
431
+ return { name: spec };
432
+ }
433
+ const atIdx = spec.indexOf("@");
434
+ if (atIdx !== -1) return {
435
+ name: spec.slice(0, atIdx),
436
+ tag: spec.slice(atIdx + 1)
437
+ };
438
+ return { name: spec };
439
+ }
404
440
  function extractBranchHint(url) {
405
441
  const hash = url.indexOf("#");
406
442
  if (hash === -1) return void 0;
@@ -557,21 +593,26 @@ function generateReleaseIndex(releasesOrOpts, packageName) {
557
593
  }
558
594
  return lines.join("\n");
559
595
  }
596
+ function isStubRelease(release) {
597
+ const body = (release.markdown || "").trim();
598
+ return body.length < 500 && /changelog\.md/i.test(body);
599
+ }
560
600
  function isChangelogRedirectPattern(releases) {
561
601
  const sample = releases.slice(0, 3);
562
602
  if (sample.length === 0) return false;
563
- return sample.every((r) => {
564
- const body = (r.markdown || "").trim();
565
- return body.length < 500 && /changelog\.md/i.test(body);
566
- });
603
+ return sample.every(isStubRelease);
567
604
  }
568
- async function fetchChangelog(owner, repo, ref) {
569
- for (const filename of [
570
- "CHANGELOG.md",
571
- "changelog.md",
572
- "CHANGES.md"
573
- ]) {
574
- const content = await $fetch(`https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${filename}`, {
605
+ async function fetchChangelog(owner, repo, ref, packageName) {
606
+ const paths = [];
607
+ if (packageName) {
608
+ const shortName = packageName.replace(/^@.*\//, "");
609
+ const scopeless = packageName.replace(/^@/, "").replace("/", "-");
610
+ const candidates = [...new Set([shortName, scopeless])];
611
+ for (const name of candidates) paths.push(`packages/${name}/CHANGELOG.md`);
612
+ }
613
+ paths.push("CHANGELOG.md", "changelog.md", "CHANGES.md");
614
+ for (const path of paths) {
615
+ const content = await $fetch(`https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path}`, {
575
616
  responseType: "text",
576
617
  signal: AbortSignal.timeout(1e4)
577
618
  }).catch(() => null);
@@ -583,26 +624,26 @@ async function fetchReleaseNotes(owner, repo, installedVersion, gitRef, packageN
583
624
  const selected = selectReleases(await fetchAllReleases(owner, repo), packageName, installedVersion, fromDate);
584
625
  if (selected.length > 0) {
585
626
  if (isChangelogRedirectPattern(selected)) {
586
- const changelog = await fetchChangelog(owner, repo, gitRef || selected[0].tag);
627
+ const changelog = await fetchChangelog(owner, repo, gitRef || selected[0].tag, packageName);
587
628
  if (changelog) return [{
588
629
  path: "releases/CHANGELOG.md",
589
630
  content: changelog
590
631
  }];
591
632
  }
592
- const docs = selected.map((r) => {
633
+ const docs = selected.filter((r) => !isStubRelease(r)).map((r) => {
593
634
  return {
594
635
  path: `releases/${r.tag.includes("@") || r.tag.startsWith("v") ? r.tag : `v${r.tag}`}.md`,
595
636
  content: formatRelease(r, packageName)
596
637
  };
597
638
  });
598
- const changelog = await fetchChangelog(owner, repo, gitRef || selected[0].tag);
639
+ const changelog = await fetchChangelog(owner, repo, gitRef || selected[0].tag, packageName);
599
640
  if (changelog && changelog.length < 5e5) docs.push({
600
641
  path: "releases/CHANGELOG.md",
601
642
  content: changelog
602
643
  });
603
644
  return docs;
604
645
  }
605
- const changelog = await fetchChangelog(owner, repo, gitRef || "main");
646
+ const changelog = await fetchChangelog(owner, repo, gitRef || "main", packageName);
606
647
  if (!changelog) return [];
607
648
  return [{
608
649
  path: "releases/CHANGELOG.md",
@@ -1152,6 +1193,89 @@ async function fetchGitLabSkills(source, onProgress) {
1152
1193
  async function fetchRawGitLab(owner, repo, ref, path) {
1153
1194
  return $fetch(`https://gitlab.com/${owner}/${repo}/-/raw/${ref}/${path}`, { responseType: "text" }).catch(() => null);
1154
1195
  }
1196
+ async function fetchLlmsUrl(docsUrl) {
1197
+ const llmsUrl = `${new URL(docsUrl).origin}/llms.txt`;
1198
+ if (await verifyUrl(llmsUrl)) return llmsUrl;
1199
+ return null;
1200
+ }
1201
+ async function fetchLlmsTxt(url) {
1202
+ const content = await fetchText(url);
1203
+ if (!content || content.length < 50) return null;
1204
+ return {
1205
+ raw: content,
1206
+ links: parseMarkdownLinks(content)
1207
+ };
1208
+ }
1209
+ function parseMarkdownLinks(content) {
1210
+ const links = [];
1211
+ const seen = /* @__PURE__ */ new Set();
1212
+ const linkRegex = /\[([^\]]+)\]\(([^)]+\.md)\)/g;
1213
+ for (let match = linkRegex.exec(content); match !== null; match = linkRegex.exec(content)) {
1214
+ const url = match[2];
1215
+ if (!seen.has(url)) {
1216
+ seen.add(url);
1217
+ links.push({
1218
+ title: match[1],
1219
+ url
1220
+ });
1221
+ }
1222
+ }
1223
+ return links;
1224
+ }
1225
+ function isSafeUrl(url) {
1226
+ try {
1227
+ const parsed = new URL(url);
1228
+ if (parsed.protocol !== "https:") return false;
1229
+ const host = parsed.hostname;
1230
+ if (host === "localhost" || host === "127.0.0.1" || host === "::1") return false;
1231
+ if (host === "169.254.169.254") return false;
1232
+ if (/^(?:10\.|172\.(?:1[6-9]|2\d|3[01])\.|192\.168\.)/.test(host)) return false;
1233
+ if (host.startsWith("[")) return false;
1234
+ return true;
1235
+ } catch {
1236
+ return false;
1237
+ }
1238
+ }
1239
+ async function downloadLlmsDocs(llmsContent, baseUrl, onProgress) {
1240
+ const limit = pLimit(5);
1241
+ let completed = 0;
1242
+ return (await Promise.all(llmsContent.links.map((link) => limit(async () => {
1243
+ const url = link.url.startsWith("http") ? link.url : `${baseUrl.replace(/\/$/, "")}${link.url.startsWith("/") ? "" : "/"}${link.url}`;
1244
+ if (!isSafeUrl(url)) return null;
1245
+ onProgress?.(link.url, completed++, llmsContent.links.length);
1246
+ const content = await fetchText(url);
1247
+ if (content && content.length > 100) return {
1248
+ url: link.url,
1249
+ title: link.title,
1250
+ content
1251
+ };
1252
+ return null;
1253
+ })))).filter((d) => d !== null);
1254
+ }
1255
+ function normalizeLlmsLinks(content, baseUrl) {
1256
+ let normalized = content;
1257
+ if (baseUrl) {
1258
+ const escaped = baseUrl.replace(/\/$/, "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1259
+ normalized = normalized.replace(new RegExp(`\\]\\(${escaped}(/[^)]+\\.md)\\)`, "g"), "](./docs$1)");
1260
+ }
1261
+ normalized = normalized.replace(/\]\(\/([^)]+\.md)\)/g, "](./docs/$1)");
1262
+ return normalized;
1263
+ }
1264
+ function extractSections(content, patterns) {
1265
+ const sections = [];
1266
+ const parts = content.split(/\n---\n/);
1267
+ for (const part of parts) {
1268
+ const urlMatch = part.match(/^url: *(\S.*)$/m);
1269
+ if (!urlMatch) continue;
1270
+ const url = urlMatch[1];
1271
+ if (patterns.some((p) => url.includes(p))) {
1272
+ const contentStart = part.indexOf("\n", part.indexOf("url:"));
1273
+ if (contentStart > -1) sections.push(part.slice(contentStart + 1));
1274
+ }
1275
+ }
1276
+ if (sections.length === 0) return null;
1277
+ return sections.join("\n\n---\n\n");
1278
+ }
1155
1279
  const MIN_GIT_DOCS = 5;
1156
1280
  const isShallowGitDocs = (n) => n > 0 && n < MIN_GIT_DOCS;
1157
1281
  async function listFilesAtRef(owner, repo, ref) {
@@ -1299,7 +1423,8 @@ async function fetchGitDocs(owner, repo, version, packageName, repoUrl) {
1299
1423
  baseUrl: `https://raw.githubusercontent.com/${override.owner}/${override.repo}/${ref}`,
1300
1424
  ref,
1301
1425
  files,
1302
- fallback
1426
+ fallback,
1427
+ docsPrefix: `${override.path}/` !== "docs/" ? `${override.path}/` : void 0
1303
1428
  };
1304
1429
  }
1305
1430
  const tag = await findGitTag(owner, repo, version, packageName, repoUrl ? extractBranchHint(repoUrl) : void 0);
@@ -1475,89 +1600,67 @@ async function fetchReadmeContent(url) {
1475
1600
  }
1476
1601
  return fetchText(url);
1477
1602
  }
1478
- async function fetchLlmsUrl(docsUrl) {
1479
- const llmsUrl = `${new URL(docsUrl).origin}/llms.txt`;
1480
- if (await verifyUrl(llmsUrl)) return llmsUrl;
1481
- return null;
1482
- }
1483
- async function fetchLlmsTxt(url) {
1484
- const content = await fetchText(url);
1485
- if (!content || content.length < 50) return null;
1603
+ async function resolveGitHubRepo(owner, repo, onProgress) {
1604
+ onProgress?.("Fetching repo metadata");
1605
+ const repoUrl = `https://github.com/${owner}/${repo}`;
1606
+ let homepage;
1607
+ let description;
1608
+ if (isGhAvailable()) try {
1609
+ const { stdout: json } = spawnSync("gh", [
1610
+ "api",
1611
+ `repos/${owner}/${repo}`,
1612
+ "--jq",
1613
+ "{homepage: .homepage, description: .description}"
1614
+ ], {
1615
+ encoding: "utf-8",
1616
+ timeout: 1e4
1617
+ });
1618
+ if (json) {
1619
+ const data = JSON.parse(json);
1620
+ homepage = data.homepage || void 0;
1621
+ description = data.description || void 0;
1622
+ }
1623
+ } catch {}
1624
+ if (!homepage && !description) {
1625
+ const data = await $fetch(`https://api.github.com/repos/${owner}/${repo}`).catch(() => null);
1626
+ homepage = data?.homepage || void 0;
1627
+ description = data?.description || void 0;
1628
+ }
1629
+ onProgress?.("Fetching latest release");
1630
+ const releasesData = await $fetch(`https://ungh.cc/repos/${owner}/${repo}/releases`).catch(() => null);
1631
+ let version = "main";
1632
+ let releasedAt;
1633
+ const latestRelease = releasesData?.releases?.[0];
1634
+ if (latestRelease) {
1635
+ version = latestRelease.tag.replace(/^v/, "");
1636
+ releasedAt = latestRelease.publishedAt;
1637
+ }
1638
+ onProgress?.("Resolving docs");
1639
+ const gitDocs = await fetchGitDocs(owner, repo, version);
1640
+ const gitDocsUrl = gitDocs ? `${repoUrl}/tree/${gitDocs.ref}/docs` : void 0;
1641
+ const gitRef = gitDocs?.ref;
1642
+ onProgress?.("Fetching README");
1643
+ const readmeUrl = await fetchReadme(owner, repo);
1644
+ let llmsUrl;
1645
+ if (homepage) {
1646
+ onProgress?.("Checking llms.txt");
1647
+ llmsUrl = await fetchLlmsUrl(homepage).catch(() => null) ?? void 0;
1648
+ }
1649
+ if (!gitDocsUrl && !readmeUrl && !llmsUrl) return null;
1486
1650
  return {
1487
- raw: content,
1488
- links: parseMarkdownLinks(content)
1651
+ name: repo,
1652
+ version: latestRelease ? version : void 0,
1653
+ releasedAt,
1654
+ description,
1655
+ repoUrl,
1656
+ docsUrl: homepage,
1657
+ gitDocsUrl,
1658
+ gitRef,
1659
+ gitDocsFallback: gitDocs?.fallback,
1660
+ readmeUrl: readmeUrl ?? void 0,
1661
+ llmsUrl
1489
1662
  };
1490
1663
  }
1491
- function parseMarkdownLinks(content) {
1492
- const links = [];
1493
- const seen = /* @__PURE__ */ new Set();
1494
- const linkRegex = /\[([^\]]+)\]\(([^)]+\.md)\)/g;
1495
- for (let match = linkRegex.exec(content); match !== null; match = linkRegex.exec(content)) {
1496
- const url = match[2];
1497
- if (!seen.has(url)) {
1498
- seen.add(url);
1499
- links.push({
1500
- title: match[1],
1501
- url
1502
- });
1503
- }
1504
- }
1505
- return links;
1506
- }
1507
- function isSafeUrl(url) {
1508
- try {
1509
- const parsed = new URL(url);
1510
- if (parsed.protocol !== "https:") return false;
1511
- const host = parsed.hostname;
1512
- if (host === "localhost" || host === "127.0.0.1" || host === "::1") return false;
1513
- if (host === "169.254.169.254") return false;
1514
- if (/^(?:10\.|172\.(?:1[6-9]|2\d|3[01])\.|192\.168\.)/.test(host)) return false;
1515
- if (host.startsWith("[")) return false;
1516
- return true;
1517
- } catch {
1518
- return false;
1519
- }
1520
- }
1521
- async function downloadLlmsDocs(llmsContent, baseUrl, onProgress) {
1522
- const limit = pLimit(5);
1523
- let completed = 0;
1524
- return (await Promise.all(llmsContent.links.map((link) => limit(async () => {
1525
- const url = link.url.startsWith("http") ? link.url : `${baseUrl.replace(/\/$/, "")}${link.url.startsWith("/") ? "" : "/"}${link.url}`;
1526
- if (!isSafeUrl(url)) return null;
1527
- onProgress?.(link.url, completed++, llmsContent.links.length);
1528
- const content = await fetchText(url);
1529
- if (content && content.length > 100) return {
1530
- url: link.url,
1531
- title: link.title,
1532
- content
1533
- };
1534
- return null;
1535
- })))).filter((d) => d !== null);
1536
- }
1537
- function normalizeLlmsLinks(content, baseUrl) {
1538
- let normalized = content;
1539
- if (baseUrl) {
1540
- const escaped = baseUrl.replace(/\/$/, "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1541
- normalized = normalized.replace(new RegExp(`\\]\\(${escaped}(/[^)]+\\.md)\\)`, "g"), "](./docs$1)");
1542
- }
1543
- normalized = normalized.replace(/\]\(\/([^)]+\.md)\)/g, "](./docs/$1)");
1544
- return normalized;
1545
- }
1546
- function extractSections(content, patterns) {
1547
- const sections = [];
1548
- const parts = content.split(/\n---\n/);
1549
- for (const part of parts) {
1550
- const urlMatch = part.match(/^url: *(\S.*)$/m);
1551
- if (!urlMatch) continue;
1552
- const url = urlMatch[1];
1553
- if (patterns.some((p) => url.includes(p))) {
1554
- const contentStart = part.indexOf("\n", part.indexOf("url:"));
1555
- if (contentStart > -1) sections.push(part.slice(contentStart + 1));
1556
- }
1557
- }
1558
- if (sections.length === 0) return null;
1559
- return sections.join("\n\n---\n\n");
1560
- }
1561
1664
  async function searchNpmPackages(query, size = 5) {
1562
1665
  const data = await $fetch(`https://registry.npmjs.org/-/v1/search?text=${encodeURIComponent(query)}&size=${size}`).catch(() => null);
1563
1666
  if (!data?.objects?.length) return [];
@@ -1948,6 +2051,6 @@ function getInstalledSkillVersion(skillDir) {
1948
2051
  if (!existsSync(skillPath)) return null;
1949
2052
  return readFileSync(skillPath, "utf-8").match(/^version:\s*"?([^"\n]+)"?/m)?.[1] || null;
1950
2053
  }
1951
- export { resolveEntryFiles as A, $fetch as B, fetchReadme as C, fetchGitSkills as D, validateGitDocsWithLlms as E, compareSemver as F, parseGitHubUrl as G, fetchText as H, fetchReleaseNotes as I, formatIssueAsMarkdown as J, verifyUrl as K, generateReleaseIndex as L, formatDiscussionAsMarkdown as M, generateDiscussionIndex as N, parseGitSkillInput as O, fetchBlogReleases as P, isPrerelease as R, fetchGitHubRepoMeta as S, isShallowGitDocs as T, isGitHubRepoUrl as U, extractBranchHint as V, normalizeRepoUrl as W, isGhAvailable as X, generateIssueIndex as Y, fetchLlmsUrl as _, getInstalledSkillVersion as a, MIN_GIT_DOCS as b, readLocalPackageInfo as c, resolvePackageDocs as d, resolvePackageDocsWithAttempts as f, fetchLlmsTxt as g, extractSections as h, fetchPkgDist as i, fetchGitHubDiscussions as j, parseSkillFrontmatterName as k, resolveInstalledVersion as l, downloadLlmsDocs as m, fetchNpmPackage as n, parseVersionSpecifier as o, searchNpmPackages as p, fetchGitHubIssues as q, fetchNpmRegistryMeta as r, readLocalDependencies as s, fetchLatestVersion as t, resolveLocalPackageDocs as u, normalizeLlmsLinks as v, fetchReadmeContent as w, fetchGitDocs as x, parseMarkdownLinks as y, parseSemver as z };
2054
+ export { SHARED_SKILLS_DIR as $, parseSkillFrontmatterName as A, parseSemver as B, extractSections as C, parseMarkdownLinks as D, normalizeLlmsLinks as E, fetchBlogReleases as F, normalizeRepoUrl as G, extractBranchHint as H, compareSemver as I, verifyUrl as J, parseGitHubUrl as K, fetchReleaseNotes as L, fetchGitHubDiscussions as M, formatDiscussionAsMarkdown as N, fetchGitSkills as O, generateDiscussionIndex as P, isGhAvailable as Q, generateReleaseIndex as R, downloadLlmsDocs as S, fetchLlmsUrl as T, fetchText as U, $fetch as V, isGitHubRepoUrl as W, formatIssueAsMarkdown as X, fetchGitHubIssues as Y, generateIssueIndex as Z, fetchReadme as _, getInstalledSkillVersion as a, resolveGitHubRepo as b, readLocalPackageInfo as c, resolvePackageDocs as d, getSharedSkillsDir as et, resolvePackageDocsWithAttempts as f, fetchGitHubRepoMeta as g, fetchGitDocs as h, fetchPkgDist as i, resolveEntryFiles as j, parseGitSkillInput as k, resolveInstalledVersion as l, MIN_GIT_DOCS as m, fetchNpmPackage as n, semverGt as nt, parseVersionSpecifier as o, searchNpmPackages as p, parsePackageSpec as q, fetchNpmRegistryMeta as r, readLocalDependencies as s, fetchLatestVersion as t, mapInsert as tt, resolveLocalPackageDocs as u, fetchReadmeContent as v, fetchLlmsTxt as w, validateGitDocsWithLlms as x, isShallowGitDocs as y, isPrerelease as z };
1952
2055
 
1953
2056
  //# sourceMappingURL=npm.mjs.map