docs2ai 0.1.4 → 0.1.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/README.md +26 -4
- package/dist/cli.mjs +185 -55
- package/dist/cli.mjs.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -45,19 +45,37 @@ docs2ai list # show configured sources
|
|
|
45
45
|
|
|
46
46
|
Once you've crawled documentation, `docs2ai serve` starts an MCP server that lets AI coding tools query your docs directly.
|
|
47
47
|
|
|
48
|
+
> **Prerequisite:** Install docs2ai globally (`npm install -g docs2ai`) or use `npx` to run it without installing. The setup examples below use `npx`, which downloads the package automatically if needed.
|
|
49
|
+
|
|
48
50
|
### Quick start
|
|
49
51
|
|
|
50
52
|
```bash
|
|
51
53
|
# 1. Crawl some docs
|
|
52
|
-
docs2ai https://docs.stripe.com/api/charges --crawl --name stripe
|
|
54
|
+
npx docs2ai https://docs.stripe.com/api/charges --crawl --name stripe
|
|
53
55
|
|
|
54
56
|
# 2. Start the MCP server
|
|
55
|
-
docs2ai serve
|
|
57
|
+
npx docs2ai serve
|
|
56
58
|
```
|
|
57
59
|
|
|
58
60
|
### Claude Code
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
```bash
|
|
63
|
+
claude mcp add --scope project docs2ai -- npx docs2ai serve -d .ai/docs/
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
That's it. Run `/mcp` inside Claude Code to verify the server is connected.
|
|
67
|
+
|
|
68
|
+
Use `--scope user` instead to make it available across all your projects.
|
|
69
|
+
|
|
70
|
+
### Cursor
|
|
71
|
+
|
|
72
|
+
Open Cursor Settings (`Cmd+,` / `Ctrl+,`) → **MCP** → **+ Add new MCP server**, then:
|
|
73
|
+
|
|
74
|
+
- **Name**: `docs2ai`
|
|
75
|
+
- **Type**: `command`
|
|
76
|
+
- **Command**: `npx docs2ai serve -d .ai/docs/`
|
|
77
|
+
|
|
78
|
+
Alternatively, create a `.cursor/mcp.json` file at your project root:
|
|
61
79
|
|
|
62
80
|
```json
|
|
63
81
|
{
|
|
@@ -70,7 +88,11 @@ Add to `.mcp.json` in your project root:
|
|
|
70
88
|
}
|
|
71
89
|
```
|
|
72
90
|
|
|
73
|
-
|
|
91
|
+
Restart Cursor for the server to be picked up. A green dot next to the server name in Settings → MCP confirms it's running.
|
|
92
|
+
|
|
93
|
+
### Available tools
|
|
94
|
+
|
|
95
|
+
Once connected, your AI assistant has access to:
|
|
74
96
|
|
|
75
97
|
- **`list_sources`** — see all available documentation sources
|
|
76
98
|
- **`list_pages`** — list pages within a source
|
package/dist/cli.mjs
CHANGED
|
@@ -51,13 +51,17 @@ function loadDocs(docsDir) {
|
|
|
51
51
|
continue;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
const loadedSource = {
|
|
55
55
|
name: sourceManifest.name,
|
|
56
56
|
url: sourceManifest.url,
|
|
57
57
|
platform: sourceManifest.platform,
|
|
58
58
|
fetchedAt: sourceManifest.fetched_at,
|
|
59
59
|
pageCount
|
|
60
|
-
}
|
|
60
|
+
};
|
|
61
|
+
if (sourceManifest.display_name) loadedSource.displayName = sourceManifest.display_name;
|
|
62
|
+
if (sourceManifest.description) loadedSource.description = sourceManifest.description;
|
|
63
|
+
if (sourceManifest.icon_url !== void 0) loadedSource.iconUrl = sourceManifest.icon_url;
|
|
64
|
+
sources.push(loadedSource);
|
|
61
65
|
}
|
|
62
66
|
return { sources, pages };
|
|
63
67
|
}
|
|
@@ -135,7 +139,10 @@ function createMcpServer(docsDir) {
|
|
|
135
139
|
url: s.url,
|
|
136
140
|
platform: s.platform,
|
|
137
141
|
fetchedAt: s.fetchedAt,
|
|
138
|
-
pageCount: s.pageCount
|
|
142
|
+
pageCount: s.pageCount,
|
|
143
|
+
...s.displayName && { displayName: s.displayName },
|
|
144
|
+
...s.description && { description: s.description },
|
|
145
|
+
...s.iconUrl !== void 0 && { iconUrl: s.iconUrl }
|
|
139
146
|
})),
|
|
140
147
|
null,
|
|
141
148
|
2
|
|
@@ -234,19 +241,50 @@ import { defineCommand as defineCommand6, runMain, runCommand } from "citty";
|
|
|
234
241
|
// src/commands/fetch.ts
|
|
235
242
|
import { defineCommand } from "citty";
|
|
236
243
|
import { dirname as dirname3 } from "path";
|
|
237
|
-
import
|
|
238
|
-
import * as
|
|
244
|
+
import consola2 from "consola";
|
|
245
|
+
import * as cheerio5 from "cheerio";
|
|
239
246
|
|
|
240
247
|
// src/pipeline/fetcher.ts
|
|
241
248
|
import { ofetch } from "ofetch";
|
|
249
|
+
import consola from "consola";
|
|
250
|
+
var BROWSER_RETRY_CODES = /* @__PURE__ */ new Set([403, 406, 429]);
|
|
251
|
+
var CHALLENGE_PATTERNS = [
|
|
252
|
+
"verify you are human",
|
|
253
|
+
"just a moment",
|
|
254
|
+
"checking your browser",
|
|
255
|
+
"attention required",
|
|
256
|
+
"enable javascript and cookies"
|
|
257
|
+
];
|
|
258
|
+
function isChallengeContent(html) {
|
|
259
|
+
const lower = html.toLowerCase();
|
|
260
|
+
return CHALLENGE_PATTERNS.some((p) => lower.includes(p));
|
|
261
|
+
}
|
|
242
262
|
async function fetchPage(url) {
|
|
243
|
-
|
|
244
|
-
|
|
263
|
+
try {
|
|
264
|
+
return await ofetch(url, { responseType: "text" });
|
|
265
|
+
} catch (err) {
|
|
266
|
+
const status = err?.response?.status ?? err?.statusCode;
|
|
267
|
+
if (status && BROWSER_RETRY_CODES.has(status)) {
|
|
268
|
+
consola.warn(
|
|
269
|
+
`Static fetch returned ${status}, retrying with browser...`
|
|
270
|
+
);
|
|
271
|
+
return fetchWithBrowser(url);
|
|
272
|
+
}
|
|
273
|
+
throw err;
|
|
274
|
+
}
|
|
245
275
|
}
|
|
246
276
|
async function fetchWithBrowser(url) {
|
|
247
|
-
|
|
277
|
+
const playwright = await loadPlaywright();
|
|
278
|
+
let html = await launchAndFetch(playwright, url, true);
|
|
279
|
+
if (isChallengeContent(html)) {
|
|
280
|
+
consola.warn("Bot protection detected, retrying with visible browser...");
|
|
281
|
+
html = await launchAndFetch(playwright, url, false);
|
|
282
|
+
}
|
|
283
|
+
return html;
|
|
284
|
+
}
|
|
285
|
+
async function loadPlaywright() {
|
|
248
286
|
try {
|
|
249
|
-
|
|
287
|
+
return await import("playwright");
|
|
250
288
|
} catch {
|
|
251
289
|
const err = new Error(
|
|
252
290
|
"Playwright is not installed. Run:\n npm install -D playwright && npx playwright install chromium"
|
|
@@ -254,13 +292,14 @@ async function fetchWithBrowser(url) {
|
|
|
254
292
|
err.code = "ERR_PLAYWRIGHT_NOT_INSTALLED";
|
|
255
293
|
throw err;
|
|
256
294
|
}
|
|
257
|
-
|
|
295
|
+
}
|
|
296
|
+
async function launchAndFetch(playwright, url, headless) {
|
|
297
|
+
const browser = await playwright.chromium.launch({ headless });
|
|
258
298
|
try {
|
|
259
299
|
const page = await browser.newPage();
|
|
260
|
-
await page.goto(url, { waitUntil: "
|
|
261
|
-
await page.waitForTimeout(
|
|
262
|
-
|
|
263
|
-
return html;
|
|
300
|
+
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 3e4 });
|
|
301
|
+
await page.waitForTimeout(2e3);
|
|
302
|
+
return await page.content();
|
|
264
303
|
} finally {
|
|
265
304
|
await browser.close();
|
|
266
305
|
}
|
|
@@ -492,9 +531,13 @@ function extract(html, url) {
|
|
|
492
531
|
return { content: selectorContent, title, platform };
|
|
493
532
|
}
|
|
494
533
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
534
|
+
let article = null;
|
|
535
|
+
try {
|
|
536
|
+
const { document } = parseHTML(html);
|
|
537
|
+
const reader = new Readability(document);
|
|
538
|
+
article = reader.parse();
|
|
539
|
+
} catch {
|
|
540
|
+
}
|
|
498
541
|
const content = article?.content || $("body").html() || html;
|
|
499
542
|
return {
|
|
500
543
|
content,
|
|
@@ -857,14 +900,23 @@ function delay(ms) {
|
|
|
857
900
|
// src/pipeline/manifest.ts
|
|
858
901
|
import { readFileSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
859
902
|
import { join as join2 } from "path";
|
|
860
|
-
function buildSourceManifest(name, url, platform, pages) {
|
|
861
|
-
|
|
903
|
+
function buildSourceManifest(name, url, platform, pages, siteMeta) {
|
|
904
|
+
const manifest = {
|
|
862
905
|
name,
|
|
863
906
|
url,
|
|
864
907
|
platform,
|
|
865
908
|
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
866
909
|
pages
|
|
867
910
|
};
|
|
911
|
+
if (siteMeta) {
|
|
912
|
+
manifest.display_name = siteMeta.displayName;
|
|
913
|
+
manifest.description = siteMeta.description;
|
|
914
|
+
manifest.icon_url = siteMeta.iconUrl;
|
|
915
|
+
manifest.og_image = siteMeta.ogImage;
|
|
916
|
+
manifest.language = siteMeta.language;
|
|
917
|
+
manifest.page_count = pages.length;
|
|
918
|
+
}
|
|
919
|
+
return manifest;
|
|
868
920
|
}
|
|
869
921
|
function writeSourceManifest(manifest, outputDir) {
|
|
870
922
|
mkdirSync2(outputDir, { recursive: true });
|
|
@@ -898,6 +950,73 @@ function updateRootManifest(rootDir, entry) {
|
|
|
898
950
|
);
|
|
899
951
|
}
|
|
900
952
|
|
|
953
|
+
// src/pipeline/meta-extractor.ts
|
|
954
|
+
import * as cheerio4 from "cheerio";
|
|
955
|
+
function extractSiteMeta(html, url) {
|
|
956
|
+
const $ = cheerio4.load(html);
|
|
957
|
+
const origin = new URL(url).origin;
|
|
958
|
+
return {
|
|
959
|
+
displayName: extractDisplayName($, url),
|
|
960
|
+
description: extractDescription($),
|
|
961
|
+
iconUrl: extractIconUrl($, origin),
|
|
962
|
+
ogImage: extractOgImage($, origin),
|
|
963
|
+
language: extractLanguage($)
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
function nonEmpty(value) {
|
|
967
|
+
return value?.trim() || void 0;
|
|
968
|
+
}
|
|
969
|
+
function extractDisplayName($, url) {
|
|
970
|
+
const ogSiteName = nonEmpty($('meta[property="og:site_name"]').attr("content"));
|
|
971
|
+
if (ogSiteName) return ogSiteName;
|
|
972
|
+
const appName = nonEmpty($('meta[name="application-name"]').attr("content"));
|
|
973
|
+
if (appName) return appName;
|
|
974
|
+
const title = nonEmpty($("title").text());
|
|
975
|
+
if (title) {
|
|
976
|
+
const parts = title.split(/\s[-|—]\s/);
|
|
977
|
+
return parts[0].trim();
|
|
978
|
+
}
|
|
979
|
+
return new URL(url).hostname;
|
|
980
|
+
}
|
|
981
|
+
function extractDescription($) {
|
|
982
|
+
const ogDesc = nonEmpty($('meta[property="og:description"]').attr("content"));
|
|
983
|
+
if (ogDesc) return ogDesc;
|
|
984
|
+
const metaDesc = nonEmpty($('meta[name="description"]').attr("content"));
|
|
985
|
+
if (metaDesc) return metaDesc;
|
|
986
|
+
return "";
|
|
987
|
+
}
|
|
988
|
+
function extractIconUrl($, origin) {
|
|
989
|
+
const selectors = [
|
|
990
|
+
'link[rel="apple-touch-icon"]',
|
|
991
|
+
'link[rel="icon"][type="image/svg+xml"]',
|
|
992
|
+
'link[rel="icon"]',
|
|
993
|
+
'link[rel="shortcut icon"]'
|
|
994
|
+
];
|
|
995
|
+
for (const selector of selectors) {
|
|
996
|
+
const href = nonEmpty($(selector).attr("href"));
|
|
997
|
+
if (href) return resolveUrl(href, origin);
|
|
998
|
+
}
|
|
999
|
+
return `${origin}/favicon.ico`;
|
|
1000
|
+
}
|
|
1001
|
+
function extractOgImage($, origin) {
|
|
1002
|
+
const ogImage = nonEmpty($('meta[property="og:image"]').attr("content"));
|
|
1003
|
+
if (ogImage) return resolveUrl(ogImage, origin);
|
|
1004
|
+
return null;
|
|
1005
|
+
}
|
|
1006
|
+
function extractLanguage($) {
|
|
1007
|
+
const htmlLang = nonEmpty($("html").attr("lang"));
|
|
1008
|
+
if (htmlLang) return htmlLang;
|
|
1009
|
+
const ogLocale = nonEmpty($('meta[property="og:locale"]').attr("content"));
|
|
1010
|
+
if (ogLocale) return ogLocale;
|
|
1011
|
+
return null;
|
|
1012
|
+
}
|
|
1013
|
+
function resolveUrl(href, origin) {
|
|
1014
|
+
if (href.startsWith("http://") || href.startsWith("https://")) return href;
|
|
1015
|
+
if (href.startsWith("//")) return `https:${href}`;
|
|
1016
|
+
if (href.startsWith("/")) return `${origin}${href}`;
|
|
1017
|
+
return `${origin}/${href}`;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
901
1020
|
// src/commands/fetch.ts
|
|
902
1021
|
function resolveOutputMode(output, shouldCrawl, name) {
|
|
903
1022
|
if (!shouldCrawl) {
|
|
@@ -952,9 +1071,9 @@ var fetchCommand = defineCommand({
|
|
|
952
1071
|
const { mode, outputPath, outputDir } = resolveOutputMode(output, shouldCrawl, name);
|
|
953
1072
|
const silent = mode === "single-file" && !outputPath;
|
|
954
1073
|
if (shouldCrawl) {
|
|
955
|
-
if (!silent)
|
|
1074
|
+
if (!silent) consola2.start(`Crawling from ${url} (max depth: ${maxDepth})...`);
|
|
956
1075
|
const firstHtml = await fetchPage(url);
|
|
957
|
-
const $ =
|
|
1076
|
+
const $ = cheerio5.load(firstHtml);
|
|
958
1077
|
const platformId = resolve(url, $);
|
|
959
1078
|
const strategy = getStrategy(platformId);
|
|
960
1079
|
const navLinkSelector = strategy.navLinkSelector();
|
|
@@ -963,11 +1082,11 @@ var fetchCommand = defineCommand({
|
|
|
963
1082
|
navLinkSelector,
|
|
964
1083
|
discoverUrls: strategy.discoverUrls?.bind(strategy),
|
|
965
1084
|
onPageFetched: (pageUrl, current, total) => {
|
|
966
|
-
if (!silent)
|
|
1085
|
+
if (!silent) consola2.info(`[${current}/${total}] ${pageUrl}`);
|
|
967
1086
|
}
|
|
968
1087
|
});
|
|
969
1088
|
const { pages, effectivePrefix } = crawlResult;
|
|
970
|
-
if (!silent)
|
|
1089
|
+
if (!silent) consola2.success(`Crawled ${pages.length} pages`);
|
|
971
1090
|
if (mode === "directory") {
|
|
972
1091
|
const pageEntries = pages.map((page) => {
|
|
973
1092
|
const { content, title, platform } = extract(page.html, page.url);
|
|
@@ -976,15 +1095,20 @@ var fetchCommand = defineCommand({
|
|
|
976
1095
|
});
|
|
977
1096
|
const firstPlatform = pageEntries[0]?.platform || "generic";
|
|
978
1097
|
const manifestPages = writePages(pageEntries, outputDir, effectivePrefix);
|
|
979
|
-
const
|
|
1098
|
+
const siteMeta = extractSiteMeta(firstHtml, url);
|
|
1099
|
+
const sourceManifest = buildSourceManifest(name, url, firstPlatform, manifestPages, siteMeta);
|
|
980
1100
|
writeSourceManifest(sourceManifest, outputDir);
|
|
981
1101
|
const rootDir = dirname3(outputDir.replace(/\/$/, ""));
|
|
982
1102
|
updateRootManifest(rootDir, {
|
|
983
1103
|
name,
|
|
984
1104
|
path: name + "/",
|
|
985
|
-
fetched_at: sourceManifest.fetched_at
|
|
1105
|
+
fetched_at: sourceManifest.fetched_at,
|
|
1106
|
+
display_name: siteMeta.displayName,
|
|
1107
|
+
description: siteMeta.description,
|
|
1108
|
+
icon_url: siteMeta.iconUrl,
|
|
1109
|
+
page_count: manifestPages.length
|
|
986
1110
|
});
|
|
987
|
-
|
|
1111
|
+
consola2.success(`Written ${pages.length} pages to ${outputDir}`);
|
|
988
1112
|
} else {
|
|
989
1113
|
const sections = [];
|
|
990
1114
|
let firstTitle = "";
|
|
@@ -1008,14 +1132,14 @@ ${md}`);
|
|
|
1008
1132
|
title: firstTitle,
|
|
1009
1133
|
platform: firstPlatform
|
|
1010
1134
|
});
|
|
1011
|
-
if (!silent)
|
|
1135
|
+
if (!silent) consola2.success(`Written to ${outputPath}`);
|
|
1012
1136
|
}
|
|
1013
1137
|
} else {
|
|
1014
|
-
if (!silent)
|
|
1138
|
+
if (!silent) consola2.start(`Fetching ${url}...`);
|
|
1015
1139
|
let html = await fetchPage(url);
|
|
1016
1140
|
const { content, title, platform } = extract(html, url);
|
|
1017
1141
|
if (content.trim().length < 200) {
|
|
1018
|
-
if (!silent)
|
|
1142
|
+
if (!silent) consola2.warn("Content looks thin, retrying with browser...");
|
|
1019
1143
|
try {
|
|
1020
1144
|
html = await fetchWithBrowser(url);
|
|
1021
1145
|
const result = extract(html, url);
|
|
@@ -1025,26 +1149,26 @@ ${md}`);
|
|
|
1025
1149
|
title: result.title || title,
|
|
1026
1150
|
platform: result.platform
|
|
1027
1151
|
});
|
|
1028
|
-
if (!silent)
|
|
1152
|
+
if (!silent) consola2.success(`Written to ${outputPath}`);
|
|
1029
1153
|
return;
|
|
1030
1154
|
} catch (err) {
|
|
1031
1155
|
if (err?.code === "ERR_PLAYWRIGHT_NOT_INSTALLED") {
|
|
1032
|
-
|
|
1156
|
+
consola2.warn(
|
|
1033
1157
|
"This page may require a browser to render. Install Playwright:\n npm install -D playwright && npx playwright install chromium"
|
|
1034
1158
|
);
|
|
1035
1159
|
} else {
|
|
1036
|
-
|
|
1160
|
+
consola2.warn("Browser fallback failed, using static content.");
|
|
1037
1161
|
}
|
|
1038
1162
|
}
|
|
1039
1163
|
}
|
|
1040
|
-
if (!silent)
|
|
1164
|
+
if (!silent) consola2.success(`Extracted content (platform: ${platform})`);
|
|
1041
1165
|
const markdown = transform(content);
|
|
1042
1166
|
write(markdown, outputPath, {
|
|
1043
1167
|
sourceUrl: url,
|
|
1044
1168
|
title,
|
|
1045
1169
|
platform
|
|
1046
1170
|
});
|
|
1047
|
-
if (!silent)
|
|
1171
|
+
if (!silent) consola2.success(`Written to ${outputPath}`);
|
|
1048
1172
|
}
|
|
1049
1173
|
}
|
|
1050
1174
|
});
|
|
@@ -1052,7 +1176,7 @@ ${md}`);
|
|
|
1052
1176
|
// src/commands/add.ts
|
|
1053
1177
|
import { defineCommand as defineCommand2 } from "citty";
|
|
1054
1178
|
import { join as join4 } from "path";
|
|
1055
|
-
import
|
|
1179
|
+
import consola3 from "consola";
|
|
1056
1180
|
|
|
1057
1181
|
// src/config/manager.ts
|
|
1058
1182
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, existsSync } from "fs";
|
|
@@ -1167,8 +1291,8 @@ var addCommand = defineCommand2({
|
|
|
1167
1291
|
}
|
|
1168
1292
|
addSource(config, { name, url, crawl: shouldCrawl, maxDepth, output });
|
|
1169
1293
|
saveConfig(config, configPath);
|
|
1170
|
-
|
|
1171
|
-
|
|
1294
|
+
consola3.success(`Added source "${name}" \u2192 ${url}`);
|
|
1295
|
+
consola3.info(`Config: ${configPath}`);
|
|
1172
1296
|
}
|
|
1173
1297
|
});
|
|
1174
1298
|
|
|
@@ -1176,8 +1300,8 @@ var addCommand = defineCommand2({
|
|
|
1176
1300
|
import { defineCommand as defineCommand3 } from "citty";
|
|
1177
1301
|
import { join as join5, dirname as dirname5 } from "path";
|
|
1178
1302
|
import { mkdirSync as mkdirSync3 } from "fs";
|
|
1179
|
-
import * as
|
|
1180
|
-
import
|
|
1303
|
+
import * as cheerio6 from "cheerio";
|
|
1304
|
+
import consola4 from "consola";
|
|
1181
1305
|
var updateCommand = defineCommand3({
|
|
1182
1306
|
meta: {
|
|
1183
1307
|
name: "update",
|
|
@@ -1192,7 +1316,7 @@ var updateCommand = defineCommand3({
|
|
|
1192
1316
|
async run({ args }) {
|
|
1193
1317
|
const result = loadConfig();
|
|
1194
1318
|
if (!result) {
|
|
1195
|
-
|
|
1319
|
+
consola4.error("No .docs2ai.yaml found. Run `docs2ai add <url>` first.");
|
|
1196
1320
|
process.exit(1);
|
|
1197
1321
|
}
|
|
1198
1322
|
const { config, configPath } = result;
|
|
@@ -1201,18 +1325,18 @@ var updateCommand = defineCommand3({
|
|
|
1201
1325
|
const sources = filterName ? config.sources.filter((s) => s.name === filterName) : config.sources;
|
|
1202
1326
|
if (sources.length === 0) {
|
|
1203
1327
|
if (filterName) {
|
|
1204
|
-
|
|
1328
|
+
consola4.error(`Source "${filterName}" not found in config.`);
|
|
1205
1329
|
} else {
|
|
1206
|
-
|
|
1330
|
+
consola4.error("No sources configured.");
|
|
1207
1331
|
}
|
|
1208
1332
|
process.exit(1);
|
|
1209
1333
|
}
|
|
1210
1334
|
for (const source of sources) {
|
|
1211
1335
|
const isDirectoryOutput = !source.output.endsWith(".md");
|
|
1212
|
-
|
|
1336
|
+
consola4.start(`Updating "${source.name}" from ${source.url}...`);
|
|
1213
1337
|
if (source.crawl) {
|
|
1214
1338
|
const firstHtml = await fetchPage(source.url);
|
|
1215
|
-
const $ =
|
|
1339
|
+
const $ = cheerio6.load(firstHtml);
|
|
1216
1340
|
const platformId = resolve(source.url, $);
|
|
1217
1341
|
const strategy = getStrategy(platformId);
|
|
1218
1342
|
const crawlResult = await crawl(source.url, {
|
|
@@ -1220,7 +1344,7 @@ var updateCommand = defineCommand3({
|
|
|
1220
1344
|
navLinkSelector: strategy.navLinkSelector(),
|
|
1221
1345
|
discoverUrls: strategy.discoverUrls?.bind(strategy),
|
|
1222
1346
|
onPageFetched: (url, current, total) => {
|
|
1223
|
-
|
|
1347
|
+
consola4.info(` [${current}/${total}] ${url}`);
|
|
1224
1348
|
}
|
|
1225
1349
|
});
|
|
1226
1350
|
const { pages, effectivePrefix } = crawlResult;
|
|
@@ -1233,20 +1357,26 @@ var updateCommand = defineCommand3({
|
|
|
1233
1357
|
});
|
|
1234
1358
|
const firstPlatform = pageEntries[0]?.platform || "generic";
|
|
1235
1359
|
const manifestPages = writePages(pageEntries, outputDir, effectivePrefix);
|
|
1360
|
+
const siteMeta = extractSiteMeta(firstHtml, source.url);
|
|
1236
1361
|
const sourceManifest = buildSourceManifest(
|
|
1237
1362
|
source.name,
|
|
1238
1363
|
source.url,
|
|
1239
1364
|
firstPlatform,
|
|
1240
|
-
manifestPages
|
|
1365
|
+
manifestPages,
|
|
1366
|
+
siteMeta
|
|
1241
1367
|
);
|
|
1242
1368
|
writeSourceManifest(sourceManifest, outputDir);
|
|
1243
1369
|
const rootDir = join5(configDir, config.outputDir);
|
|
1244
1370
|
updateRootManifest(rootDir, {
|
|
1245
1371
|
name: source.name,
|
|
1246
1372
|
path: source.output,
|
|
1247
|
-
fetched_at: sourceManifest.fetched_at
|
|
1373
|
+
fetched_at: sourceManifest.fetched_at,
|
|
1374
|
+
display_name: siteMeta.displayName,
|
|
1375
|
+
description: siteMeta.description,
|
|
1376
|
+
icon_url: siteMeta.iconUrl,
|
|
1377
|
+
page_count: manifestPages.length
|
|
1248
1378
|
});
|
|
1249
|
-
|
|
1379
|
+
consola4.success(`Updated "${source.name}" \u2192 ${outputDir} (${pages.length} pages)`);
|
|
1250
1380
|
} else {
|
|
1251
1381
|
const outputPath = join5(configDir, config.outputDir, source.output);
|
|
1252
1382
|
mkdirSync3(dirname5(outputPath), { recursive: true });
|
|
@@ -1272,7 +1402,7 @@ ${md}`);
|
|
|
1272
1402
|
title: firstTitle,
|
|
1273
1403
|
platform: firstPlatform
|
|
1274
1404
|
});
|
|
1275
|
-
|
|
1405
|
+
consola4.success(`Updated "${source.name}" \u2192 ${outputPath}`);
|
|
1276
1406
|
}
|
|
1277
1407
|
} else {
|
|
1278
1408
|
const outputPath = join5(configDir, config.outputDir, source.output);
|
|
@@ -1285,7 +1415,7 @@ ${md}`);
|
|
|
1285
1415
|
title,
|
|
1286
1416
|
platform
|
|
1287
1417
|
});
|
|
1288
|
-
|
|
1418
|
+
consola4.success(`Updated "${source.name}" \u2192 ${outputPath}`);
|
|
1289
1419
|
}
|
|
1290
1420
|
}
|
|
1291
1421
|
}
|
|
@@ -1293,7 +1423,7 @@ ${md}`);
|
|
|
1293
1423
|
|
|
1294
1424
|
// src/commands/list.ts
|
|
1295
1425
|
import { defineCommand as defineCommand4 } from "citty";
|
|
1296
|
-
import
|
|
1426
|
+
import consola5 from "consola";
|
|
1297
1427
|
var listCommand = defineCommand4({
|
|
1298
1428
|
meta: {
|
|
1299
1429
|
name: "list",
|
|
@@ -1302,15 +1432,15 @@ var listCommand = defineCommand4({
|
|
|
1302
1432
|
run() {
|
|
1303
1433
|
const result = loadConfig();
|
|
1304
1434
|
if (!result) {
|
|
1305
|
-
|
|
1435
|
+
consola5.info("No .docs2ai.yaml found. Run `docs2ai add <url>` to get started.");
|
|
1306
1436
|
return;
|
|
1307
1437
|
}
|
|
1308
1438
|
const { config, configPath } = result;
|
|
1309
|
-
|
|
1310
|
-
|
|
1439
|
+
consola5.info(`Config: ${configPath}`);
|
|
1440
|
+
consola5.info(`Output dir: ${config.outputDir}
|
|
1311
1441
|
`);
|
|
1312
1442
|
if (config.sources.length === 0) {
|
|
1313
|
-
|
|
1443
|
+
consola5.info("No sources configured.");
|
|
1314
1444
|
return;
|
|
1315
1445
|
}
|
|
1316
1446
|
for (const source of config.sources) {
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp/loader.ts","../src/mcp/search.ts","../src/mcp/server.ts","../src/cli.ts","../src/commands/fetch.ts","../src/pipeline/fetcher.ts","../src/pipeline/extractor.ts","../src/platforms/mintlify.ts","../src/platforms/docusaurus.ts","../src/platforms/readme.ts","../src/platforms/gitbook.ts","../src/platforms/generic.ts","../src/platforms/registry.ts","../src/pipeline/resolver.ts","../src/pipeline/transformer.ts","../src/pipeline/writer.ts","../src/utils/slug.ts","../src/crawl/crawler.ts","../src/utils/url.ts","../src/crawl/boundary.ts","../src/pipeline/manifest.ts","../src/commands/add.ts","../src/config/manager.ts","../src/commands/update.ts","../src/commands/list.ts","../src/commands/serve.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport matter from \"gray-matter\";\n\n/** A single loaded documentation page. */\nexport interface LoadedPage {\n source: string;\n path: string;\n title: string;\n url: string;\n platform: string;\n fetchedAt: string;\n content: string;\n}\n\n/** A loaded documentation source (from _index.json). */\nexport interface LoadedSource {\n name: string;\n url: string;\n platform: string;\n fetchedAt: string;\n pageCount: number;\n}\n\n/** All loaded documentation data. */\nexport interface LoadedDocs {\n sources: LoadedSource[];\n pages: LoadedPage[];\n}\n\ninterface RootManifest {\n sources: { name: string; path: string; fetched_at: string }[];\n}\n\ninterface SourceManifest {\n name: string;\n url: string;\n platform: string;\n fetched_at: string;\n pages: { title: string; path: string }[];\n}\n\n/**\n * Load all documentation from a docs directory into memory.\n * Reads manifest.json → each source's _index.json → each page's .md file.\n * Silently skips missing files (no console output — critical for stdio transport).\n */\nexport function loadDocs(docsDir: string): LoadedDocs {\n const sources: LoadedSource[] = [];\n const pages: LoadedPage[] = [];\n\n let rootManifest: RootManifest;\n try {\n const raw = readFileSync(join(docsDir, \"manifest.json\"), \"utf-8\");\n rootManifest = JSON.parse(raw);\n } catch {\n return { sources: [], pages: [] };\n }\n\n for (const sourceEntry of rootManifest.sources) {\n const sourceDir = join(docsDir, sourceEntry.path);\n let sourceManifest: SourceManifest;\n try {\n const raw = readFileSync(join(sourceDir, \"_index.json\"), \"utf-8\");\n sourceManifest = JSON.parse(raw);\n } catch {\n continue;\n }\n\n let pageCount = 0;\n\n for (const pageEntry of sourceManifest.pages) {\n try {\n const raw = readFileSync(join(sourceDir, pageEntry.path), \"utf-8\");\n const parsed = matter(raw);\n pages.push({\n source: sourceManifest.name,\n path: pageEntry.path,\n title: pageEntry.title,\n url: String(parsed.data.source || \"\"),\n platform: String(parsed.data.platform || sourceManifest.platform),\n fetchedAt: String(parsed.data.fetched_at || sourceManifest.fetched_at),\n content: parsed.content.trim(),\n });\n pageCount++;\n } catch {\n continue;\n }\n }\n\n sources.push({\n name: sourceManifest.name,\n url: sourceManifest.url,\n platform: sourceManifest.platform,\n fetchedAt: sourceManifest.fetched_at,\n pageCount,\n });\n }\n\n return { sources, pages };\n}\n","import MiniSearch from \"minisearch\";\nimport type { LoadedPage } from \"./loader\";\n\n/** A search result with metadata (no content). */\nexport interface SearchResult {\n source: string;\n path: string;\n title: string;\n url: string;\n score: number;\n}\n\n/** Options for searching the index. */\nexport interface SearchOptions {\n source?: string;\n limit?: number;\n}\n\n/** Search index over loaded documentation pages. */\nexport interface SearchIndex {\n search(query: string, options?: SearchOptions): SearchResult[];\n}\n\n/**\n * Build a full-text search index over all loaded pages.\n * Indexes title and content fields with prefix and fuzzy matching.\n */\nexport function buildSearchIndex(pages: LoadedPage[]): SearchIndex {\n const miniSearch = new MiniSearch<LoadedPage>({\n fields: [\"title\", \"content\"],\n storeFields: [\"source\", \"path\", \"title\", \"url\"],\n idField: \"id\",\n });\n\n const documents = pages.map((page, i) => ({\n id: String(i),\n ...page,\n }));\n\n miniSearch.addAll(documents);\n\n return {\n search(query: string, options?: SearchOptions): SearchResult[] {\n if (!query.trim()) return [];\n\n const filter = options?.source\n ? (result: { source: string }) => result.source === options.source\n : undefined;\n\n const results = miniSearch.search(query, {\n prefix: true,\n fuzzy: 0.2,\n filter: filter as any,\n });\n\n const limit = options?.limit ?? 10;\n\n return results.slice(0, limit).map((r) => ({\n source: r.source as string,\n path: r.path as string,\n title: r.title as string,\n url: r.url as string,\n score: r.score,\n }));\n },\n };\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { loadDocs } from \"./loader\";\nimport { buildSearchIndex } from \"./search\";\n\n/**\n * Create an MCP server that exposes documentation tools.\n * Eagerly loads all docs and builds a search index at creation time.\n */\nexport function createMcpServer(docsDir: string): McpServer {\n const docs = loadDocs(docsDir);\n const searchIndex = buildSearchIndex(docs.pages);\n\n const server = new McpServer({\n name: \"doc2ctx\",\n version: \"0.1.0\",\n });\n\n server.tool(\n \"list_sources\",\n \"List all documentation sources available in the docs directory\",\n {},\n async () => {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n docs.sources.map((s) => ({\n name: s.name,\n url: s.url,\n platform: s.platform,\n fetchedAt: s.fetchedAt,\n pageCount: s.pageCount,\n })),\n null,\n 2\n ),\n },\n ],\n };\n }\n );\n\n server.tool(\n \"list_pages\",\n \"List all pages for a specific documentation source\",\n { source: z.string().describe(\"Name of the documentation source\") },\n async ({ source }) => {\n const sourceEntry = docs.sources.find((s) => s.name === source);\n if (!sourceEntry) {\n return {\n content: [{ type: \"text\", text: `Source \"${source}\" not found. Use list_sources to see available sources.` }],\n isError: true,\n };\n }\n const pages = docs.pages\n .filter((p) => p.source === source)\n .map((p) => ({ title: p.title, path: p.path }));\n return {\n content: [{ type: \"text\", text: JSON.stringify(pages, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"read_page\",\n \"Read the full markdown content of a documentation page\",\n {\n source: z.string().describe(\"Name of the documentation source\"),\n path: z.string().describe(\"Path of the page within the source (from list_pages)\"),\n },\n async ({ source, path }) => {\n const page = docs.pages.find((p) => p.source === source && p.path === path);\n if (!page) {\n return {\n content: [{ type: \"text\", text: `Page \"${path}\" not found in source \"${source}\". Use list_pages to see available pages.` }],\n isError: true,\n };\n }\n return {\n content: [{ type: \"text\", text: page.content }],\n };\n }\n );\n\n server.tool(\n \"search_docs\",\n \"Search across all documentation pages by keyword\",\n {\n query: z.string().describe(\"Search query\"),\n source: z.string().optional().describe(\"Filter results to a specific source\"),\n limit: z.number().optional().describe(\"Maximum number of results (default 10)\"),\n },\n async ({ query, source, limit }) => {\n if (!query.trim()) {\n return {\n content: [{ type: \"text\", text: \"Search query cannot be empty.\" }],\n isError: true,\n };\n }\n const results = searchIndex.search(query, { source, limit });\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n results.map((r) => ({\n source: r.source,\n path: r.path,\n title: r.title,\n score: r.score,\n })),\n null,\n 2\n ),\n },\n ],\n };\n }\n );\n\n return server;\n}\n","import { defineCommand, runMain, runCommand } from \"citty\";\nimport { fetchCommand } from \"./commands/fetch\";\nimport { addCommand } from \"./commands/add\";\nimport { updateCommand } from \"./commands/update\";\nimport { listCommand } from \"./commands/list\";\nimport { serveCommand } from \"./commands/serve\";\n\nconst subCommands: Record<string, any> = {\n add: addCommand,\n update: updateCommand,\n list: listCommand,\n serve: serveCommand,\n};\n\n// Check if first non-flag arg is a subcommand\nconst firstArg = process.argv[2];\nconst isSubCommand = firstArg && firstArg in subCommands;\n\nif (isSubCommand) {\n // Let citty handle subcommand routing\n const main = defineCommand({\n meta: {\n name: \"docs2ai\",\n version: \"0.1.0\",\n description: \"Convert documentation URLs into AI-ready Markdown files\",\n },\n subCommands,\n });\n runMain(main);\n} else if (firstArg && !firstArg.startsWith(\"-\") && firstArg !== \"--help\") {\n // Treat as a URL — run fetch command directly\n runCommand(fetchCommand, { rawArgs: process.argv.slice(2) });\n} else {\n // No args or --help — show usage\n const main = defineCommand({\n meta: {\n name: \"docs2ai\",\n version: \"0.1.0\",\n description: \"Convert documentation URLs into AI-ready Markdown files\",\n },\n subCommands,\n run() {\n console.log(\"Usage: docs2ai <url> [-o output.md] [--crawl]\");\n console.log(\" docs2ai add <url> [--name name] [--crawl]\");\n console.log(\" docs2ai update [--name name]\");\n console.log(\" docs2ai list\");\n console.log(\" docs2ai serve [-d dir]\");\n console.log(\"\\nRun `docs2ai --help` for full usage.\");\n },\n });\n runMain(main);\n}\n","import { defineCommand } from \"citty\";\nimport { dirname } from \"node:path\";\nimport consola from \"consola\";\nimport * as cheerio from \"cheerio\";\nimport { fetchPage, fetchWithBrowser } from \"../pipeline/fetcher\";\nimport { extract } from \"../pipeline/extractor\";\nimport { transform } from \"../pipeline/transformer\";\nimport { write, writePages } from \"../pipeline/writer\";\nimport { crawl } from \"../crawl/crawler\";\nimport { resolve } from \"../pipeline/resolver\";\nimport { getStrategy } from \"../platforms/registry\";\nimport { slugFromUrl } from \"../utils/url\";\nimport {\n buildSourceManifest,\n writeSourceManifest,\n updateRootManifest,\n} from \"../pipeline/manifest\";\n\n/**\n * Determine whether crawl output should go to a directory (one file per page)\n * or a single stitched file.\n */\nfunction resolveOutputMode(\n output: string | undefined,\n shouldCrawl: boolean,\n name: string\n): { mode: \"single-file\" | \"directory\"; outputPath: string | undefined; outputDir: string } {\n if (!shouldCrawl) {\n return { mode: \"single-file\", outputPath: output, outputDir: \"\" };\n }\n\n // Crawl + explicit .md output → single file (backward compat)\n if (output && output.endsWith(\".md\")) {\n return { mode: \"single-file\", outputPath: output, outputDir: \"\" };\n }\n\n // Crawl + explicit directory path\n if (output) {\n const dir = output.endsWith(\"/\") ? output : output + \"/\";\n return { mode: \"directory\", outputPath: undefined, outputDir: dir };\n }\n\n // Crawl + no output → default directory\n return { mode: \"directory\", outputPath: undefined, outputDir: `.ai/docs/${name}/` };\n}\n\nexport const fetchCommand = defineCommand({\n meta: {\n name: \"fetch\",\n description: \"Fetch a documentation URL and convert to Markdown\",\n },\n args: {\n url: {\n type: \"positional\",\n description: \"Documentation URL to convert\",\n required: true,\n },\n output: {\n type: \"string\",\n alias: \"o\",\n description: \"Output file path or directory\",\n },\n name: {\n type: \"string\",\n description: \"Name for this source (auto-derived from hostname if omitted)\",\n },\n crawl: {\n type: \"boolean\",\n description: \"Follow sidebar/nav links\",\n default: false,\n },\n \"max-depth\": {\n type: \"string\",\n description: \"Maximum crawl depth\",\n default: \"2\",\n },\n },\n async run({ args }) {\n const url = args.url as string;\n const output = args.output as string | undefined;\n const shouldCrawl = args.crawl as boolean;\n const maxDepth = parseInt(args[\"max-depth\"] as string, 10);\n const name = (args.name as string) || slugFromUrl(url);\n\n const { mode, outputPath, outputDir } = resolveOutputMode(output, shouldCrawl, name);\n const silent = mode === \"single-file\" && !outputPath;\n\n if (shouldCrawl) {\n if (!silent) consola.start(`Crawling from ${url} (max depth: ${maxDepth})...`);\n\n // Fetch first page to resolve platform and get navLinkSelector\n const firstHtml = await fetchPage(url);\n const $ = cheerio.load(firstHtml);\n const platformId = resolve(url, $);\n const strategy = getStrategy(platformId);\n const navLinkSelector = strategy.navLinkSelector();\n\n const crawlResult = await crawl(url, {\n maxDepth,\n navLinkSelector,\n discoverUrls: strategy.discoverUrls?.bind(strategy),\n onPageFetched: (pageUrl, current, total) => {\n if (!silent) consola.info(`[${current}/${total}] ${pageUrl}`);\n },\n });\n\n const { pages, effectivePrefix } = crawlResult;\n if (!silent) consola.success(`Crawled ${pages.length} pages`);\n\n if (mode === \"directory\") {\n // Directory mode: one .md file per page + manifests\n const pageEntries = pages.map((page) => {\n const { content, title, platform } = extract(page.html, page.url);\n const md = transform(content);\n return { url: page.url, title, platform, markdown: md };\n });\n\n const firstPlatform = pageEntries[0]?.platform || \"generic\";\n const manifestPages = writePages(pageEntries, outputDir, effectivePrefix);\n\n const sourceManifest = buildSourceManifest(name, url, firstPlatform, manifestPages);\n writeSourceManifest(sourceManifest, outputDir);\n\n // Update root manifest in the parent directory\n const rootDir = dirname(outputDir.replace(/\\/$/, \"\"));\n updateRootManifest(rootDir, {\n name,\n path: name + \"/\",\n fetched_at: sourceManifest.fetched_at,\n });\n\n consola.success(`Written ${pages.length} pages to ${outputDir}`);\n } else {\n // Single-file mode: stitch all pages together\n const sections: string[] = [];\n let firstTitle = \"\";\n let firstPlatform = \"\";\n\n for (const page of pages) {\n const { content, title, platform } = extract(page.html, page.url);\n if (!firstTitle) {\n firstTitle = title;\n firstPlatform = platform;\n }\n const md = transform(content);\n sections.push(`## ${title}\\n\\nSource: ${page.url}\\n\\n${md}`);\n }\n\n const markdown = sections.join(\"\\n\\n---\\n\\n\");\n\n write(markdown, outputPath, {\n sourceUrl: url,\n title: firstTitle,\n platform: firstPlatform,\n });\n\n if (!silent) consola.success(`Written to ${outputPath}`);\n }\n } else {\n if (!silent) consola.start(`Fetching ${url}...`);\n let html = await fetchPage(url);\n\n const { content, title, platform } = extract(html, url);\n\n // If content is suspiciously small, try Playwright\n if (content.trim().length < 200) {\n if (!silent) consola.warn(\"Content looks thin, retrying with browser...\");\n try {\n html = await fetchWithBrowser(url);\n const result = extract(html, url);\n const markdown = transform(result.content);\n write(markdown, outputPath, {\n sourceUrl: url,\n title: result.title || title,\n platform: result.platform,\n });\n if (!silent) consola.success(`Written to ${outputPath}`);\n return;\n } catch (err: any) {\n if (err?.code === \"ERR_PLAYWRIGHT_NOT_INSTALLED\") {\n consola.warn(\n \"This page may require a browser to render. Install Playwright:\\n\" +\n \" npm install -D playwright && npx playwright install chromium\"\n );\n } else {\n consola.warn(\"Browser fallback failed, using static content.\");\n }\n }\n }\n\n if (!silent) consola.success(`Extracted content (platform: ${platform})`);\n const markdown = transform(content);\n\n write(markdown, outputPath, {\n sourceUrl: url,\n title,\n platform,\n });\n\n if (!silent) consola.success(`Written to ${outputPath}`);\n }\n },\n});\n","import { ofetch } from \"ofetch\";\n\n/**\n * Fetch the raw HTML of a documentation page.\n * Uses static fetch by default.\n */\nexport async function fetchPage(url: string): Promise<string> {\n const html = await ofetch(url, { responseType: \"text\" });\n return html;\n}\n\n/**\n * Fetch a page using Playwright for JS-rendered sites.\n * Playwright is an optional dependency — throws a typed error if not installed.\n */\nexport async function fetchWithBrowser(url: string): Promise<string> {\n let playwright;\n try {\n playwright = await import(\"playwright\");\n } catch {\n const err = new Error(\n \"Playwright is not installed. Run:\\n npm install -D playwright && npx playwright install chromium\"\n );\n (err as any).code = \"ERR_PLAYWRIGHT_NOT_INSTALLED\";\n throw err;\n }\n\n const browser = await playwright.chromium.launch({ headless: true });\n try {\n const page = await browser.newPage();\n await page.goto(url, { waitUntil: \"networkidle\" });\n // Extra buffer for late-loading content\n await page.waitForTimeout(1000);\n const html = await page.content();\n return html;\n } finally {\n await browser.close();\n }\n}\n","import * as cheerio from \"cheerio\";\nimport { Readability } from \"@mozilla/readability\";\nimport { parseHTML } from \"linkedom\";\nimport { resolve } from \"./resolver\";\nimport { getStrategy } from \"../platforms/registry\";\nimport type { PlatformId } from \"../platforms/base\";\n\nexport interface ExtractResult {\n content: string;\n title: string;\n platform: PlatformId;\n}\n\n/**\n * Extract meaningful content from raw HTML.\n * Uses platform-specific selectors when available, falls back to Readability.\n */\nexport function extract(html: string, url: string): ExtractResult {\n const $ = cheerio.load(html);\n const platform = resolve(url, $);\n const strategy = getStrategy(platform);\n\n const title = extractTitle($);\n\n // Non-generic platforms: use selector-based extraction first\n if (platform !== \"generic\") {\n for (const sel of strategy.removeSelectors()) {\n $(sel).remove();\n }\n\n const contentEl = $(strategy.contentSelector()).first();\n const selectorContent = contentEl.html();\n\n if (selectorContent && selectorContent.trim().length >= 100) {\n return { content: selectorContent, title, platform };\n }\n // Fall through to Readability if selector extraction yields too little\n }\n\n // Generic / fallback: Readability extraction\n const { document } = parseHTML(html);\n const reader = new Readability(document as any);\n const article = reader.parse();\n\n const content = article?.content || $(\"body\").html() || html;\n\n return {\n content,\n title: title || article?.title || \"\",\n platform,\n };\n}\n\n/**\n * Extract page title from common sources.\n */\nfunction extractTitle($: cheerio.CheerioAPI): string {\n const h1 = $(\"h1\").first().text().trim();\n if (h1) return h1;\n\n const ogTitle = $('meta[property=\"og:title\"]').attr(\"content\")?.trim();\n if (ogTitle) return ogTitle;\n\n return $(\"title\").text().trim();\n}\n","import * as cheerio from \"cheerio\";\nimport type { CheerioAPI } from \"cheerio\";\nimport type { PlatformStrategy } from \"./base\";\n\nexport const mintlify: PlatformStrategy = {\n id: \"mintlify\",\n\n detect(url: string, $: CheerioAPI): boolean {\n if ($('meta[name=\"generator\"][content*=\"Mintlify\"]').length > 0) return true;\n if ($(\"script[src*='mintlify']\").length > 0) return true;\n if ($(\"[data-mintlify]\").length > 0) return true;\n return false;\n },\n\n contentSelector(): string {\n return \"article, main\";\n },\n\n removeSelectors(): string[] {\n return [\n \"nav\",\n \"header\",\n \"footer\",\n \"[role='navigation']\",\n \".sidebar\",\n \"[class*='sidebar']\",\n \"[class*='cookie']\",\n \"[class*='banner']\",\n \"script\",\n \"style\",\n ];\n },\n\n navLinkSelector(): string | null {\n return \"nav a[href], .sidebar a[href], [class*='sidebar'] a[href]\";\n },\n\n discoverUrls(html: string, baseUrl: string): string[] {\n const $ = cheerio.load(html);\n const paths = new Set<string>();\n\n // Mintlify uses Next.js — sidebar nav is in __next_f script data, not <a> tags.\n // The data contains escaped JSON like \\\"href\\\":\\\"/api-reference/checkouts/create\\\"\n $(\"script\").each((_, el) => {\n const text = $(el).html() || \"\";\n // Match escaped JSON paths: \\\"href\\\":\\\"/some-path\\\"\n const escaped = /\\\\?\"href\\\\?\"\\s*:\\s*\\\\?\"(\\/[a-z0-9][a-z0-9\\/-]*)\\\\?\"/g;\n let match = escaped.exec(text);\n while (match !== null) {\n paths.add(match[1]);\n match = escaped.exec(text);\n }\n });\n\n // Resolve to absolute URLs\n const origin = new URL(baseUrl).origin;\n // Determine docs base path (e.g. /docs from /docs/api-reference/intro)\n const basePath = new URL(baseUrl).pathname.split(\"/\").slice(0, 2).join(\"/\");\n\n return [...paths].map((p) => {\n if (p.startsWith(basePath)) {\n return origin + p;\n }\n return origin + basePath + p;\n });\n },\n};\n","import type { CheerioAPI } from \"cheerio\";\nimport type { PlatformStrategy } from \"./base\";\n\nexport const docusaurus: PlatformStrategy = {\n id: \"docusaurus\",\n\n detect(url: string, $: CheerioAPI): boolean {\n if ($('meta[name=\"generator\"][content*=\"Docusaurus\"]').length > 0)\n return true;\n if ($(\".theme-doc-sidebar-container\").length > 0) return true;\n if ($('meta[name=\"docusaurus_locale\"]').length > 0) return true;\n return false;\n },\n\n contentSelector(): string {\n return \"article, [role='main'], .theme-doc-markdown\";\n },\n\n removeSelectors(): string[] {\n return [\n \".navbar\",\n \"footer\",\n \".theme-doc-toc-desktop\",\n \".theme-doc-sidebar-container\",\n \".pagination-nav\",\n \".theme-doc-breadcrumbs\",\n \"nav\",\n \"script\",\n \"style\",\n ];\n },\n\n navLinkSelector(): string | null {\n return \".menu__link[href]\";\n },\n};\n","import type { CheerioAPI } from \"cheerio\";\nimport type { PlatformStrategy } from \"./base\";\n\nexport const readme: PlatformStrategy = {\n id: \"readme\",\n\n detect(url: string, $: CheerioAPI): boolean {\n let rmClassCount = 0;\n $(\"[class]\").each((_, el) => {\n const cls = $(el).attr(\"class\") || \"\";\n if (/\\brm-/.test(cls)) rmClassCount++;\n });\n if (rmClassCount > 2) return true;\n if ($(\".rm-Article\").length > 0) return true;\n if ($(\".rm-Markdown\").length > 0) return true;\n return false;\n },\n\n contentSelector(): string {\n return \".markdown-body, .rm-Article, .rm-Markdown\";\n },\n\n removeSelectors(): string[] {\n return [\n \"nav\",\n \"header\",\n \"footer\",\n \".rm-Sidebar\",\n \".rm-TableOfContents\",\n \"[class*='cookie']\",\n \"script\",\n \"style\",\n ];\n },\n\n navLinkSelector(): string | null {\n return \".rm-Sidebar a[href]\";\n },\n};\n","import type { CheerioAPI } from \"cheerio\";\nimport type { PlatformStrategy } from \"./base\";\n\nexport const gitbook: PlatformStrategy = {\n id: \"gitbook\",\n\n detect(url: string, $: CheerioAPI): boolean {\n if ($('meta[name=\"generator\"][content*=\"GitBook\"]').length > 0) return true;\n try {\n const parsed = new URL(url);\n if (parsed.hostname.endsWith(\".gitbook.io\")) return true;\n } catch {\n // invalid URL, skip host check\n }\n if ($('[data-testid=\"page.contentEditor\"]').length > 0) return true;\n return false;\n },\n\n contentSelector(): string {\n return '[data-testid=\"page.contentEditor\"], main, article';\n },\n\n removeSelectors(): string[] {\n return [\n \"nav\",\n \"header\",\n \"footer\",\n \"[class*='sidebar']\",\n \"[class*='toc']\",\n \"[class*='cookie']\",\n \"script\",\n \"style\",\n ];\n },\n\n navLinkSelector(): string | null {\n return \"nav a[href], aside a[href]\";\n },\n};\n","import type { CheerioAPI } from \"cheerio\";\nimport type { PlatformStrategy } from \"./base\";\n\nexport const generic: PlatformStrategy = {\n id: \"generic\",\n\n detect(_url: string, _$: CheerioAPI): boolean {\n return true;\n },\n\n contentSelector(): string {\n return \"article, main, [role='main'], .content\";\n },\n\n removeSelectors(): string[] {\n return [\n \"nav\",\n \"header\",\n \"footer\",\n \"[role='navigation']\",\n \"[class*='sidebar']\",\n \"[class*='cookie']\",\n \"[class*='banner']\",\n \"script\",\n \"style\",\n \"noscript\",\n ];\n },\n\n navLinkSelector(): string | null {\n return null;\n },\n};\n","import type { PlatformId, PlatformStrategy } from \"./base\";\nimport { mintlify } from \"./mintlify\";\nimport { docusaurus } from \"./docusaurus\";\nimport { readme } from \"./readme\";\nimport { gitbook } from \"./gitbook\";\nimport { generic } from \"./generic\";\n\n/** Ordered list of platform strategies. Generic must be last (always matches). */\nexport const platformStrategies: PlatformStrategy[] = [\n mintlify,\n docusaurus,\n readme,\n gitbook,\n generic,\n];\n\n/** Get a strategy by its platform ID. */\nexport function getStrategy(id: PlatformId): PlatformStrategy {\n const strategy = platformStrategies.find((s) => s.id === id);\n if (!strategy) {\n throw new Error(`Unknown platform: ${id}`);\n }\n return strategy;\n}\n","import type { CheerioAPI } from \"cheerio\";\nimport type { PlatformId } from \"../platforms/base\";\nimport { platformStrategies } from \"../platforms/registry\";\n\n/**\n * Detect which documentation platform a page belongs to.\n * Tries platform-specific strategies in order, falls back to generic.\n */\nexport function resolve(url: string, $: CheerioAPI): PlatformId {\n for (const strategy of platformStrategies) {\n if (strategy.detect(url, $)) {\n return strategy.id;\n }\n }\n return \"generic\";\n}\n","import TurndownService from \"turndown\";\nimport { gfm } from \"turndown-plugin-gfm\";\n\n/**\n * Convert clean HTML to Markdown.\n */\nexport function transform(html: string): string {\n const td = new TurndownService({\n headingStyle: \"atx\",\n codeBlockStyle: \"fenced\",\n bulletListMarker: \"-\",\n });\n\n td.use(gfm);\n\n addCalloutRule(td);\n addTabbedContentRule(td);\n addCodeBlockLangRule(td);\n addHiddenElementRule(td);\n\n return td.turndown(html);\n}\n\nfunction isElement(node: TurndownService.Node): node is HTMLElement {\n return node.nodeType === 1;\n}\n\nfunction getAttr(node: TurndownService.Node, attr: string): string {\n if (isElement(node)) {\n return node.getAttribute(attr) || \"\";\n }\n return \"\";\n}\n\nfunction getTagName(node: TurndownService.Node): string {\n if (isElement(node)) {\n return node.tagName.toLowerCase();\n }\n return \"\";\n}\n\n/**\n * Convert callouts/admonitions to blockquotes.\n * Matches: aside, .admonition, .callout, .alert, [role=\"alert\"]\n */\nfunction addCalloutRule(td: TurndownService): void {\n td.addRule(\"callouts\", {\n filter(node) {\n if (!isElement(node)) return false;\n const tag = getTagName(node);\n if (tag === \"aside\") return true;\n const cls = getAttr(node, \"class\");\n if (\n /\\b(admonition|callout|alert|notice|warning|info|tip|note|caution|danger)\\b/i.test(\n cls\n )\n )\n return true;\n if (getAttr(node, \"role\") === \"alert\") return true;\n return false;\n },\n replacement(content, node) {\n const cls = getAttr(node, \"class\").toLowerCase();\n let type = \"Note\";\n if (/warning|caution/.test(cls)) type = \"Warning\";\n else if (/danger|error/.test(cls)) type = \"Danger\";\n else if (/tip|success/.test(cls)) type = \"Tip\";\n else if (/info/.test(cls)) type = \"Info\";\n\n const lines = content.trim().split(\"\\n\");\n const quoted = lines.map((line) => `> ${line}`).join(\"\\n\");\n return `\\n> **${type}**\\n${quoted}\\n\\n`;\n },\n });\n}\n\n/**\n * Convert tabbed content into labeled sections.\n * Matches: .tab-panel, .tabpanel, [role=\"tabpanel\"]\n */\nfunction addTabbedContentRule(td: TurndownService): void {\n td.addRule(\"tabbed-content\", {\n filter(node) {\n if (!isElement(node)) return false;\n const cls = getAttr(node, \"class\");\n if (/\\b(tab-panel|tabpanel|tabs__item)\\b/i.test(cls)) return true;\n if (getAttr(node, \"role\") === \"tabpanel\") return true;\n return false;\n },\n replacement(content, node) {\n const label =\n getAttr(node, \"aria-label\") ||\n getAttr(node, \"data-label\") ||\n getAttr(node, \"data-value\") ||\n \"\";\n if (label) {\n return `\\n**${label}**\\n\\n${content.trim()}\\n\\n`;\n }\n return `\\n${content.trim()}\\n\\n`;\n },\n });\n}\n\n/**\n * Ensure code blocks with data-language/data-lang produce proper fenced blocks.\n */\nfunction addCodeBlockLangRule(td: TurndownService): void {\n td.addRule(\"code-block-lang\", {\n filter(node) {\n if (!isElement(node)) return false;\n if (getTagName(node) !== \"pre\") return false;\n const codeEl = node.querySelector(\"code\");\n if (!codeEl) return false;\n const lang =\n getAttr(node, \"data-language\") ||\n getAttr(node, \"data-lang\") ||\n (codeEl.getAttribute(\"data-language\") || \"\") ||\n (codeEl.getAttribute(\"data-lang\") || \"\");\n return lang.length > 0;\n },\n replacement(_content, node) {\n if (!isElement(node)) return _content;\n const codeEl = node.querySelector(\"code\")!;\n const lang =\n getAttr(node, \"data-language\") ||\n getAttr(node, \"data-lang\") ||\n (codeEl.getAttribute(\"data-language\") || \"\") ||\n (codeEl.getAttribute(\"data-lang\") || \"\");\n const code = codeEl.textContent || \"\";\n return `\\n\\`\\`\\`${lang}\\n${code}\\n\\`\\`\\`\\n`;\n },\n });\n}\n\n/**\n * Remove hidden elements (display:none) except tab panels.\n */\nfunction addHiddenElementRule(td: TurndownService): void {\n td.addRule(\"hidden-elements\", {\n filter(node) {\n if (!isElement(node)) return false;\n const style = getAttr(node, \"style\");\n if (!/display\\s*:\\s*none/i.test(style)) return false;\n // Don't remove tab panels — they're hidden but contain valid content\n const cls = getAttr(node, \"class\");\n if (/\\b(tab-panel|tabpanel)\\b/i.test(cls)) return false;\n if (getAttr(node, \"role\") === \"tabpanel\") return false;\n return true;\n },\n replacement() {\n return \"\";\n },\n });\n}\n","import { writeFileSync, mkdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport { filePathForPage } from \"../utils/slug\";\n\nexport interface WriterOptions {\n sourceUrl: string;\n title: string;\n platform: string;\n}\n\n/**\n * Write Markdown with frontmatter to a file or stdout.\n */\nexport function write(\n markdown: string,\n outputPath: string | undefined,\n options: WriterOptions\n): void {\n const content = matter.stringify(markdown, {\n source: options.sourceUrl,\n fetched_at: new Date().toISOString(),\n platform: options.platform,\n title: options.title,\n docs2ai_version: \"0.1.0\",\n });\n\n if (outputPath) {\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, content, \"utf-8\");\n } else {\n process.stdout.write(content);\n }\n}\n\n/**\n * Write a single page's Markdown with frontmatter to a file path (always writes to disk).\n */\nexport function writePage(\n markdown: string,\n filePath: string,\n options: WriterOptions\n): void {\n const content = matter.stringify(markdown, {\n source: options.sourceUrl,\n fetched_at: new Date().toISOString(),\n platform: options.platform,\n title: options.title,\n docs2ai_version: \"0.1.0\",\n });\n\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, content, \"utf-8\");\n}\n\nexport interface PageEntry {\n url: string;\n title: string;\n platform: string;\n markdown: string;\n}\n\n/**\n * Write multiple crawled pages to a directory, one .md file per page.\n * Returns manifest page entries (title + relative path) for each written page.\n */\nexport function writePages(\n pages: PageEntry[],\n outputDir: string,\n basePrefix: string\n): { title: string; path: string }[] {\n const usedPaths = new Set<string>();\n const entries: { title: string; path: string }[] = [];\n\n for (const page of pages) {\n let relPath = filePathForPage(page.url, basePrefix);\n\n // Handle slug collisions by appending a numeric suffix\n if (usedPaths.has(relPath)) {\n const base = relPath.replace(/\\.md$/, \"\");\n let i = 2;\n while (usedPaths.has(`${base}-${i}.md`)) i++;\n relPath = `${base}-${i}.md`;\n }\n usedPaths.add(relPath);\n\n const filePath = join(outputDir, relPath);\n writePage(page.markdown, filePath, {\n sourceUrl: page.url,\n title: page.title,\n platform: page.platform,\n });\n\n entries.push({ title: page.title, path: relPath });\n }\n\n return entries;\n}\n","/**\n * Derive a filename slug from a URL's pathname.\n * Strips leading/trailing slashes and returns the last segment.\n */\nexport function slugFromPathname(url: string): string {\n const parsed = new URL(url);\n const pathname = parsed.pathname.replace(/\\/+$/, \"\");\n if (!pathname || pathname === \"/\") return \"index\";\n const segments = pathname.split(\"/\").filter(Boolean);\n return segments[segments.length - 1];\n}\n\n/**\n * Compute a relative file path for a crawled page based on its URL and a base prefix.\n * Strips the basePrefix from the pathname, intermediate segments become subdirectories,\n * and the last segment becomes the filename with .md extension.\n *\n * @example filePathForPage(\"https://x.com/docs/guides/auth\", \"/docs/\") → \"guides/auth.md\"\n * @example filePathForPage(\"https://x.com/docs/getting-started\", \"/docs/\") → \"getting-started.md\"\n * @example filePathForPage(\"https://x.com/docs/\", \"/docs/\") → \"index.md\"\n */\nexport function filePathForPage(pageUrl: string, basePrefix: string): string {\n const parsed = new URL(pageUrl);\n let pathname = parsed.pathname.replace(/\\/+$/, \"\");\n\n // Strip the base prefix\n const normalizedPrefix = basePrefix.replace(/\\/+$/, \"\");\n if (pathname.startsWith(normalizedPrefix)) {\n pathname = pathname.slice(normalizedPrefix.length);\n }\n\n // Remove leading slash\n pathname = pathname.replace(/^\\/+/, \"\");\n\n if (!pathname) return \"index.md\";\n\n return pathname + \".md\";\n}\n","import * as cheerio from \"cheerio\";\nimport { fetchPage } from \"../pipeline/fetcher\";\nimport {\n getCrawlPrefix,\n computeCommonPrefix,\n isInBounds,\n normalizeUrl,\n} from \"./boundary\";\n\nexport interface CrawledPage {\n url: string;\n html: string;\n}\n\nexport interface CrawlResult {\n pages: CrawledPage[];\n effectivePrefix: string;\n}\n\nexport interface CrawlOptions {\n maxDepth: number;\n navLinkSelector?: string | null;\n /** Custom URL discovery for SPA-rendered sidebars (overrides navLinkSelector) */\n discoverUrls?: (html: string, baseUrl: string) => string[];\n onPageFetched?: (url: string, current: number, total: number) => void;\n}\n\n/**\n * Crawl documentation pages starting from a URL.\n * Follows in-bounds links via BFS up to maxDepth.\n */\nexport async function crawl(\n startUrl: string,\n options: CrawlOptions\n): Promise<CrawlResult> {\n const { origin } = getCrawlPrefix(startUrl);\n let { pathPrefix } = getCrawlPrefix(startUrl);\n const visited = new Set<string>();\n const results: CrawledPage[] = [];\n let isFirstPage = true;\n\n // BFS queue: [url, depth]\n const queue: [string, number][] = [[startUrl, 0]];\n visited.add(normalizeUrl(startUrl));\n\n while (queue.length > 0) {\n const [url, depth] = queue.shift()!;\n\n let html: string;\n try {\n html = await fetchPage(url);\n } catch {\n options.onPageFetched?.(url, results.length, results.length + queue.length);\n continue;\n }\n results.push({ url, html });\n options.onPageFetched?.(url, results.length, results.length + queue.length);\n\n if (depth < options.maxDepth) {\n // On the first page, discover all same-origin nav links to widen the boundary\n if (isFirstPage) {\n const allNavUrls = options.discoverUrls\n ? discoverSameOriginCustom(html, url, origin, options.discoverUrls)\n : discoverSameOrigin(html, url, origin, options.navLinkSelector);\n if (allNavUrls.length > 0) {\n pathPrefix = computeCommonPrefix(startUrl, allNavUrls);\n }\n isFirstPage = false;\n }\n\n const links = options.discoverUrls\n ? discoverLinksCustom(html, url, origin, pathPrefix, options.discoverUrls)\n : discoverLinks(html, url, origin, pathPrefix, options.navLinkSelector);\n for (const link of links) {\n const normalized = normalizeUrl(link);\n if (!visited.has(normalized)) {\n visited.add(normalized);\n queue.push([link, depth + 1]);\n }\n }\n }\n\n // Politeness delay between requests\n if (queue.length > 0) {\n await delay(200);\n }\n }\n\n return { pages: results, effectivePrefix: pathPrefix };\n}\n\n/**\n * Extract all in-bounds links from a page's HTML.\n * When navLinkSelector is provided, only links matching that selector are used.\n */\nfunction discoverLinks(\n html: string,\n baseUrl: string,\n origin: string,\n pathPrefix: string,\n navLinkSelector?: string | null\n): string[] {\n const $ = cheerio.load(html);\n const links: string[] = [];\n const selector = navLinkSelector || \"a[href]\";\n\n $(selector).each((_, el) => {\n const href = $(el).attr(\"href\");\n if (!href) return;\n\n try {\n const resolved = new URL(href, baseUrl).href;\n if (isInBounds(resolved, origin, pathPrefix)) {\n links.push(resolved);\n }\n } catch {\n // Invalid URL, skip\n }\n });\n\n return [...new Set(links)];\n}\n\n/**\n * Discover all same-origin links from nav (no path prefix filter).\n * Used on the first page to compute the crawl boundary from nav structure.\n */\nfunction discoverSameOrigin(\n html: string,\n baseUrl: string,\n origin: string,\n navLinkSelector?: string | null\n): string[] {\n const $ = cheerio.load(html);\n const links: string[] = [];\n const selector = navLinkSelector || \"a[href]\";\n\n $(selector).each((_, el) => {\n const href = $(el).attr(\"href\");\n if (!href) return;\n try {\n const resolved = new URL(href, baseUrl).href;\n if (new URL(resolved).origin === origin) {\n links.push(resolved);\n }\n } catch {\n // Invalid URL, skip\n }\n });\n\n return [...new Set(links)];\n}\n\n/**\n * Discover all same-origin links via custom discovery (no path prefix filter).\n */\nfunction discoverSameOriginCustom(\n html: string,\n baseUrl: string,\n origin: string,\n discoverUrls: (html: string, baseUrl: string) => string[]\n): string[] {\n const urls = discoverUrls(html, baseUrl);\n return [\n ...new Set(\n urls.filter((u) => {\n try {\n return new URL(u).origin === origin;\n } catch {\n return false;\n }\n })\n ),\n ];\n}\n\n/**\n * Extract in-bounds links using a custom discovery function from the platform strategy.\n */\nfunction discoverLinksCustom(\n html: string,\n baseUrl: string,\n origin: string,\n pathPrefix: string,\n discoverUrls: (html: string, baseUrl: string) => string[]\n): string[] {\n const urls = discoverUrls(html, baseUrl);\n return [...new Set(urls.filter((u) => isInBounds(u, origin, pathPrefix)))];\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Validate whether a string is a valid URL.\n */\nexport function isValidUrl(input: string): boolean {\n try {\n new URL(input);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Normalize a URL for deduplication: strip hash, query, trailing slash.\n */\nexport function normalizeUrl(url: string): string {\n const parsed = new URL(url);\n parsed.hash = \"\";\n parsed.search = \"\";\n return parsed.href.replace(/\\/$/, \"\");\n}\n\n/**\n * Derive a short name/slug from a URL's hostname.\n */\nexport function slugFromUrl(url: string): string {\n try {\n const parsed = new URL(url);\n return parsed.hostname.replace(/\\./g, \"-\").replace(/^www-/, \"\");\n } catch {\n return \"source\";\n }\n}\n","import { normalizeUrl } from \"../utils/url\";\n\nexport { normalizeUrl };\n\n/**\n * Determine the crawl boundary from a starting URL.\n * Links are in-bounds if they share the same origin and path prefix.\n */\nexport function getCrawlPrefix(url: string): {\n origin: string;\n pathPrefix: string;\n} {\n const parsed = new URL(url);\n const pathParts = parsed.pathname.split(\"/\");\n // Remove the last segment (the current page slug)\n pathParts.pop();\n const pathPrefix = pathParts.join(\"/\") + \"/\";\n return { origin: parsed.origin, pathPrefix };\n}\n\n/**\n * Compute the longest common path prefix between a start URL and discovered nav URLs.\n * Used to widen the crawl boundary when sidebar links span multiple sections.\n */\nexport function computeCommonPrefix(\n startUrl: string,\n navUrls: string[]\n): string {\n const startParts = new URL(startUrl).pathname.split(\"/\").filter(Boolean);\n const parts = [...startParts];\n\n for (const url of navUrls) {\n const urlParts = new URL(url).pathname.split(\"/\").filter(Boolean);\n let i = 0;\n while (i < parts.length && i < urlParts.length && parts[i] === urlParts[i]) {\n i++;\n }\n parts.length = i;\n }\n\n return \"/\" + (parts.length > 0 ? parts.join(\"/\") + \"/\" : \"\");\n}\n\n/**\n * Check whether a candidate URL falls within the crawl boundary.\n */\nexport function isInBounds(\n candidateUrl: string,\n origin: string,\n pathPrefix: string\n): boolean {\n try {\n const parsed = new URL(candidateUrl);\n return parsed.origin === origin && parsed.pathname.startsWith(pathPrefix);\n } catch {\n return false;\n }\n}\n","import { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\n\n/** Manifest for a single documentation source (written as _index.json). */\nexport interface SourceManifest {\n name: string;\n url: string;\n platform: string;\n fetched_at: string;\n pages: { title: string; path: string }[];\n}\n\n/** Root manifest listing all sources (written as manifest.json). */\nexport interface RootManifest {\n sources: { name: string; path: string; fetched_at: string }[];\n}\n\n/**\n * Build a source manifest object.\n */\nexport function buildSourceManifest(\n name: string,\n url: string,\n platform: string,\n pages: { title: string; path: string }[]\n): SourceManifest {\n return {\n name,\n url,\n platform,\n fetched_at: new Date().toISOString(),\n pages,\n };\n}\n\n/**\n * Write a source manifest (_index.json) to a directory.\n */\nexport function writeSourceManifest(\n manifest: SourceManifest,\n outputDir: string\n): void {\n mkdirSync(outputDir, { recursive: true });\n writeFileSync(\n join(outputDir, \"_index.json\"),\n JSON.stringify(manifest, null, 2) + \"\\n\",\n \"utf-8\"\n );\n}\n\n/**\n * Load the root manifest (manifest.json) from a directory.\n * Returns an empty manifest if the file doesn't exist.\n */\nexport function loadRootManifest(rootDir: string): RootManifest {\n try {\n const raw = readFileSync(join(rootDir, \"manifest.json\"), \"utf-8\");\n return JSON.parse(raw) as RootManifest;\n } catch {\n return { sources: [] };\n }\n}\n\n/**\n * Upsert a source entry in the root manifest and write it to disk.\n */\nexport function updateRootManifest(\n rootDir: string,\n entry: { name: string; path: string; fetched_at: string }\n): void {\n const manifest = loadRootManifest(rootDir);\n const idx = manifest.sources.findIndex((s) => s.name === entry.name);\n if (idx >= 0) {\n manifest.sources[idx] = entry;\n } else {\n manifest.sources.push(entry);\n }\n mkdirSync(rootDir, { recursive: true });\n writeFileSync(\n join(rootDir, \"manifest.json\"),\n JSON.stringify(manifest, null, 2) + \"\\n\",\n \"utf-8\"\n );\n}\n","import { defineCommand } from \"citty\";\nimport { join } from \"node:path\";\nimport consola from \"consola\";\nimport { loadConfig, saveConfig, addSource } from \"../config/manager\";\nimport type { Docs2aiConfig } from \"../config/schema\";\nimport { slugFromUrl } from \"../utils/url\";\n\nexport const addCommand = defineCommand({\n meta: {\n name: \"add\",\n description: \"Add a documentation source to .docs2ai.yaml\",\n },\n args: {\n url: {\n type: \"positional\",\n description: \"Documentation URL to add\",\n required: true,\n },\n name: {\n type: \"string\",\n description: \"Name for this source (auto-derived from hostname if omitted)\",\n },\n crawl: {\n type: \"boolean\",\n description: \"Enable crawl mode for this source\",\n default: false,\n },\n \"max-depth\": {\n type: \"string\",\n description: \"Maximum crawl depth\",\n default: \"2\",\n },\n output: {\n type: \"string\",\n alias: \"o\",\n description: \"Output filename or directory\",\n },\n },\n run({ args }) {\n const url = args.url as string;\n const shouldCrawl = args.crawl as boolean;\n const maxDepth = parseInt(args[\"max-depth\"] as string, 10);\n\n const name = (args.name as string) || slugFromUrl(url);\n const output =\n (args.output as string) || (shouldCrawl ? `${name}/` : `${name}.md`);\n\n const existing = loadConfig();\n let config: Docs2aiConfig;\n let configPath: string;\n\n if (existing) {\n config = existing.config;\n configPath = existing.configPath;\n } else {\n configPath = join(process.cwd(), \".docs2ai.yaml\");\n config = { version: 1, outputDir: \".ai/docs\", sources: [] };\n }\n\n addSource(config, { name, url, crawl: shouldCrawl, maxDepth, output });\n saveConfig(config, configPath);\n\n consola.success(`Added source \"${name}\" → ${url}`);\n consola.info(`Config: ${configPath}`);\n },\n});\n","import { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport yaml from \"js-yaml\";\nimport type { Docs2aiConfig, SourceConfig } from \"./schema\";\n\nconst CONFIG_FILENAME = \".docs2ai.yaml\";\n\n/**\n * Load the .docs2ai.yaml config file, searching up from cwd.\n * Returns null if no config file is found.\n */\nexport function loadConfig(startDir?: string): {\n config: Docs2aiConfig;\n configPath: string;\n} | null {\n const configPath = findConfigFile(startDir || process.cwd());\n if (!configPath) return null;\n\n const raw = readFileSync(configPath, \"utf-8\");\n const data = yaml.load(raw) as Record<string, any>;\n\n const config: Docs2aiConfig = {\n version: data.version ?? 1,\n outputDir: data.output_dir ?? \".ai/docs\",\n sources: (data.sources ?? []).map(snakeToCamelSource),\n };\n\n return { config, configPath };\n}\n\n/**\n * Save configuration to a .docs2ai.yaml file.\n */\nexport function saveConfig(config: Docs2aiConfig, configPath: string): void {\n const data = {\n version: config.version,\n output_dir: config.outputDir,\n sources: config.sources.map(camelToSnakeSource),\n };\n\n const content = yaml.dump(data, { lineWidth: -1 });\n writeFileSync(configPath, content, \"utf-8\");\n}\n\n/**\n * Add or update a source in the config (upsert by name).\n */\nexport function addSource(config: Docs2aiConfig, source: SourceConfig): void {\n const idx = config.sources.findIndex((s) => s.name === source.name);\n if (idx >= 0) {\n config.sources[idx] = source;\n } else {\n config.sources.push(source);\n }\n}\n\n/**\n * Walk up the directory tree looking for .docs2ai.yaml.\n */\nfunction findConfigFile(startDir: string): string | null {\n let dir = startDir;\n while (true) {\n const candidate = join(dir, CONFIG_FILENAME);\n if (existsSync(candidate)) return candidate;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nfunction snakeToCamelSource(s: Record<string, any>): SourceConfig {\n return {\n name: s.name ?? \"\",\n url: s.url ?? \"\",\n crawl: s.crawl ?? false,\n maxDepth: s.max_depth ?? 2,\n output: s.output ?? \"\",\n };\n}\n\nfunction camelToSnakeSource(\n s: SourceConfig\n): Record<string, string | number | boolean> {\n return {\n name: s.name,\n url: s.url,\n crawl: s.crawl,\n max_depth: s.maxDepth,\n output: s.output,\n };\n}\n","import { defineCommand } from \"citty\";\nimport { join, dirname } from \"node:path\";\nimport { mkdirSync } from \"node:fs\";\nimport * as cheerio from \"cheerio\";\nimport consola from \"consola\";\nimport { loadConfig } from \"../config/manager\";\nimport { fetchPage } from \"../pipeline/fetcher\";\nimport { extract } from \"../pipeline/extractor\";\nimport { transform } from \"../pipeline/transformer\";\nimport { write, writePages } from \"../pipeline/writer\";\nimport { crawl } from \"../crawl/crawler\";\nimport { resolve } from \"../pipeline/resolver\";\nimport { getStrategy } from \"../platforms/registry\";\nimport {\n buildSourceManifest,\n writeSourceManifest,\n updateRootManifest,\n} from \"../pipeline/manifest\";\n\nexport const updateCommand = defineCommand({\n meta: {\n name: \"update\",\n description: \"Refresh configured documentation sources\",\n },\n args: {\n name: {\n type: \"string\",\n description: \"Update only the named source\",\n },\n },\n async run({ args }) {\n const result = loadConfig();\n if (!result) {\n consola.error(\"No .docs2ai.yaml found. Run `docs2ai add <url>` first.\");\n process.exit(1);\n }\n\n const { config, configPath } = result;\n const configDir = dirname(configPath);\n const filterName = args.name as string | undefined;\n\n const sources = filterName\n ? config.sources.filter((s) => s.name === filterName)\n : config.sources;\n\n if (sources.length === 0) {\n if (filterName) {\n consola.error(`Source \"${filterName}\" not found in config.`);\n } else {\n consola.error(\"No sources configured.\");\n }\n process.exit(1);\n }\n\n for (const source of sources) {\n const isDirectoryOutput = !source.output.endsWith(\".md\");\n\n consola.start(`Updating \"${source.name}\" from ${source.url}...`);\n\n if (source.crawl) {\n // Fetch first page to resolve platform and get link discovery\n const firstHtml = await fetchPage(source.url);\n const $ = cheerio.load(firstHtml);\n const platformId = resolve(source.url, $);\n const strategy = getStrategy(platformId);\n\n const crawlResult = await crawl(source.url, {\n maxDepth: source.maxDepth,\n navLinkSelector: strategy.navLinkSelector(),\n discoverUrls: strategy.discoverUrls?.bind(strategy),\n onPageFetched: (url, current, total) => {\n consola.info(` [${current}/${total}] ${url}`);\n },\n });\n\n const { pages, effectivePrefix } = crawlResult;\n\n if (isDirectoryOutput) {\n // Directory mode: one .md file per page + manifests\n const outputDir = join(configDir, config.outputDir, source.output);\n const pageEntries = pages.map((page) => {\n const { content, title, platform } = extract(page.html, page.url);\n const md = transform(content);\n return { url: page.url, title, platform, markdown: md };\n });\n\n const firstPlatform = pageEntries[0]?.platform || \"generic\";\n const manifestPages = writePages(pageEntries, outputDir, effectivePrefix);\n\n const sourceManifest = buildSourceManifest(\n source.name,\n source.url,\n firstPlatform,\n manifestPages\n );\n writeSourceManifest(sourceManifest, outputDir);\n\n const rootDir = join(configDir, config.outputDir);\n updateRootManifest(rootDir, {\n name: source.name,\n path: source.output,\n fetched_at: sourceManifest.fetched_at,\n });\n\n consola.success(`Updated \"${source.name}\" → ${outputDir} (${pages.length} pages)`);\n } else {\n // Single-file mode: stitch all pages together\n const outputPath = join(configDir, config.outputDir, source.output);\n mkdirSync(dirname(outputPath), { recursive: true });\n\n const sections: string[] = [];\n let firstTitle = \"\";\n let firstPlatform = \"\";\n\n for (const page of pages) {\n const { content, title, platform } = extract(page.html, page.url);\n if (!firstTitle) {\n firstTitle = title;\n firstPlatform = platform;\n }\n const md = transform(content);\n sections.push(`## ${title}\\n\\nSource: ${page.url}\\n\\n${md}`);\n }\n\n const markdown = sections.join(\"\\n\\n---\\n\\n\");\n write(markdown, outputPath, {\n sourceUrl: source.url,\n title: firstTitle,\n platform: firstPlatform,\n });\n\n consola.success(`Updated \"${source.name}\" → ${outputPath}`);\n }\n } else {\n const outputPath = join(configDir, config.outputDir, source.output);\n mkdirSync(dirname(outputPath), { recursive: true });\n\n const html = await fetchPage(source.url);\n const { content, title, platform } = extract(html, source.url);\n const markdown = transform(content);\n write(markdown, outputPath, {\n sourceUrl: source.url,\n title,\n platform,\n });\n\n consola.success(`Updated \"${source.name}\" → ${outputPath}`);\n }\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport consola from \"consola\";\nimport { loadConfig } from \"../config/manager\";\n\nexport const listCommand = defineCommand({\n meta: {\n name: \"list\",\n description: \"List configured documentation sources\",\n },\n run() {\n const result = loadConfig();\n if (!result) {\n consola.info(\"No .docs2ai.yaml found. Run `docs2ai add <url>` to get started.\");\n return;\n }\n\n const { config, configPath } = result;\n consola.info(`Config: ${configPath}`);\n consola.info(`Output dir: ${config.outputDir}\\n`);\n\n if (config.sources.length === 0) {\n consola.info(\"No sources configured.\");\n return;\n }\n\n for (const source of config.sources) {\n const crawlInfo = source.crawl\n ? ` (crawl, depth: ${source.maxDepth})`\n : \"\";\n console.log(` ${source.name}${crawlInfo}`);\n console.log(` URL: ${source.url}`);\n console.log(` Output: ${source.output}`);\n console.log();\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { resolve } from \"node:path\";\n\nexport const serveCommand = defineCommand({\n meta: {\n name: \"serve\",\n description: \"Start an MCP server exposing documentation tools\",\n },\n args: {\n dir: {\n type: \"string\",\n alias: \"d\",\n description: \"Documentation directory to serve\",\n default: \".ai/docs/\",\n },\n },\n async run({ args }) {\n const docsDir = resolve(process.cwd(), args.dir as string);\n const { createMcpServer } = await import(\"../mcp/server\");\n const { StdioServerTransport } = await import(\n \"@modelcontextprotocol/sdk/server/stdio.js\"\n );\n const server = createMcpServer(docsDir);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n },\n});\n"],"mappings":"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","names":["readFileSync","join","matter","defineCommand","dirname","cheerio","cheerio","cheerio","resolve","writeFileSync","mkdirSync","join","dirname","markdown","defineCommand","join","consola","readFileSync","writeFileSync","join","dirname","defineCommand","join","consola","defineCommand","join","dirname","mkdirSync","cheerio","consola","defineCommand","consola","dirname","join","mkdirSync","defineCommand","consola","defineCommand","consola","defineCommand","resolve","createMcpServer","defineCommand"]}
|
|
1
|
+
{"version":3,"sources":["../src/mcp/loader.ts","../src/mcp/search.ts","../src/mcp/server.ts","../src/cli.ts","../src/commands/fetch.ts","../src/pipeline/fetcher.ts","../src/pipeline/extractor.ts","../src/platforms/mintlify.ts","../src/platforms/docusaurus.ts","../src/platforms/readme.ts","../src/platforms/gitbook.ts","../src/platforms/generic.ts","../src/platforms/registry.ts","../src/pipeline/resolver.ts","../src/pipeline/transformer.ts","../src/pipeline/writer.ts","../src/utils/slug.ts","../src/crawl/crawler.ts","../src/utils/url.ts","../src/crawl/boundary.ts","../src/pipeline/manifest.ts","../src/pipeline/meta-extractor.ts","../src/commands/add.ts","../src/config/manager.ts","../src/commands/update.ts","../src/commands/list.ts","../src/commands/serve.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport matter from \"gray-matter\";\n\n/** A single loaded documentation page. */\nexport interface LoadedPage {\n source: string;\n path: string;\n title: string;\n url: string;\n platform: string;\n fetchedAt: string;\n content: string;\n}\n\n/** A loaded documentation source (from _index.json). */\nexport interface LoadedSource {\n name: string;\n url: string;\n platform: string;\n fetchedAt: string;\n pageCount: number;\n displayName?: string;\n description?: string;\n iconUrl?: string | null;\n}\n\n/** All loaded documentation data. */\nexport interface LoadedDocs {\n sources: LoadedSource[];\n pages: LoadedPage[];\n}\n\ninterface RootManifest {\n sources: { name: string; path: string; fetched_at: string }[];\n}\n\ninterface SourceManifest {\n name: string;\n url: string;\n platform: string;\n fetched_at: string;\n pages: { title: string; path: string }[];\n display_name?: string;\n description?: string;\n icon_url?: string | null;\n}\n\n/**\n * Load all documentation from a docs directory into memory.\n * Reads manifest.json → each source's _index.json → each page's .md file.\n * Silently skips missing files (no console output — critical for stdio transport).\n */\nexport function loadDocs(docsDir: string): LoadedDocs {\n const sources: LoadedSource[] = [];\n const pages: LoadedPage[] = [];\n\n let rootManifest: RootManifest;\n try {\n const raw = readFileSync(join(docsDir, \"manifest.json\"), \"utf-8\");\n rootManifest = JSON.parse(raw);\n } catch {\n return { sources: [], pages: [] };\n }\n\n for (const sourceEntry of rootManifest.sources) {\n const sourceDir = join(docsDir, sourceEntry.path);\n let sourceManifest: SourceManifest;\n try {\n const raw = readFileSync(join(sourceDir, \"_index.json\"), \"utf-8\");\n sourceManifest = JSON.parse(raw);\n } catch {\n continue;\n }\n\n let pageCount = 0;\n\n for (const pageEntry of sourceManifest.pages) {\n try {\n const raw = readFileSync(join(sourceDir, pageEntry.path), \"utf-8\");\n const parsed = matter(raw);\n pages.push({\n source: sourceManifest.name,\n path: pageEntry.path,\n title: pageEntry.title,\n url: String(parsed.data.source || \"\"),\n platform: String(parsed.data.platform || sourceManifest.platform),\n fetchedAt: String(parsed.data.fetched_at || sourceManifest.fetched_at),\n content: parsed.content.trim(),\n });\n pageCount++;\n } catch {\n continue;\n }\n }\n\n const loadedSource: LoadedSource = {\n name: sourceManifest.name,\n url: sourceManifest.url,\n platform: sourceManifest.platform,\n fetchedAt: sourceManifest.fetched_at,\n pageCount,\n };\n if (sourceManifest.display_name) loadedSource.displayName = sourceManifest.display_name;\n if (sourceManifest.description) loadedSource.description = sourceManifest.description;\n if (sourceManifest.icon_url !== undefined) loadedSource.iconUrl = sourceManifest.icon_url;\n sources.push(loadedSource);\n }\n\n return { sources, pages };\n}\n","import MiniSearch from \"minisearch\";\nimport type { LoadedPage } from \"./loader\";\n\n/** A search result with metadata (no content). */\nexport interface SearchResult {\n source: string;\n path: string;\n title: string;\n url: string;\n score: number;\n}\n\n/** Options for searching the index. */\nexport interface SearchOptions {\n source?: string;\n limit?: number;\n}\n\n/** Search index over loaded documentation pages. */\nexport interface SearchIndex {\n search(query: string, options?: SearchOptions): SearchResult[];\n}\n\n/**\n * Build a full-text search index over all loaded pages.\n * Indexes title and content fields with prefix and fuzzy matching.\n */\nexport function buildSearchIndex(pages: LoadedPage[]): SearchIndex {\n const miniSearch = new MiniSearch<LoadedPage>({\n fields: [\"title\", \"content\"],\n storeFields: [\"source\", \"path\", \"title\", \"url\"],\n idField: \"id\",\n });\n\n const documents = pages.map((page, i) => ({\n id: String(i),\n ...page,\n }));\n\n miniSearch.addAll(documents);\n\n return {\n search(query: string, options?: SearchOptions): SearchResult[] {\n if (!query.trim()) return [];\n\n const filter = options?.source\n ? (result: { source: string }) => result.source === options.source\n : undefined;\n\n const results = miniSearch.search(query, {\n prefix: true,\n fuzzy: 0.2,\n filter: filter as any,\n });\n\n const limit = options?.limit ?? 10;\n\n return results.slice(0, limit).map((r) => ({\n source: r.source as string,\n path: r.path as string,\n title: r.title as string,\n url: r.url as string,\n score: r.score,\n }));\n },\n };\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { loadDocs } from \"./loader\";\nimport { buildSearchIndex } from \"./search\";\n\n/**\n * Create an MCP server that exposes documentation tools.\n * Eagerly loads all docs and builds a search index at creation time.\n */\nexport function createMcpServer(docsDir: string): McpServer {\n const docs = loadDocs(docsDir);\n const searchIndex = buildSearchIndex(docs.pages);\n\n const server = new McpServer({\n name: \"doc2ctx\",\n version: \"0.1.0\",\n });\n\n server.tool(\n \"list_sources\",\n \"List all documentation sources available in the docs directory\",\n {},\n async () => {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n docs.sources.map((s) => ({\n name: s.name,\n url: s.url,\n platform: s.platform,\n fetchedAt: s.fetchedAt,\n pageCount: s.pageCount,\n ...(s.displayName && { displayName: s.displayName }),\n ...(s.description && { description: s.description }),\n ...(s.iconUrl !== undefined && { iconUrl: s.iconUrl }),\n })),\n null,\n 2\n ),\n },\n ],\n };\n }\n );\n\n server.tool(\n \"list_pages\",\n \"List all pages for a specific documentation source\",\n { source: z.string().describe(\"Name of the documentation source\") },\n async ({ source }) => {\n const sourceEntry = docs.sources.find((s) => s.name === source);\n if (!sourceEntry) {\n return {\n content: [{ type: \"text\", text: `Source \"${source}\" not found. Use list_sources to see available sources.` }],\n isError: true,\n };\n }\n const pages = docs.pages\n .filter((p) => p.source === source)\n .map((p) => ({ title: p.title, path: p.path }));\n return {\n content: [{ type: \"text\", text: JSON.stringify(pages, null, 2) }],\n };\n }\n );\n\n server.tool(\n \"read_page\",\n \"Read the full markdown content of a documentation page\",\n {\n source: z.string().describe(\"Name of the documentation source\"),\n path: z.string().describe(\"Path of the page within the source (from list_pages)\"),\n },\n async ({ source, path }) => {\n const page = docs.pages.find((p) => p.source === source && p.path === path);\n if (!page) {\n return {\n content: [{ type: \"text\", text: `Page \"${path}\" not found in source \"${source}\". Use list_pages to see available pages.` }],\n isError: true,\n };\n }\n return {\n content: [{ type: \"text\", text: page.content }],\n };\n }\n );\n\n server.tool(\n \"search_docs\",\n \"Search across all documentation pages by keyword\",\n {\n query: z.string().describe(\"Search query\"),\n source: z.string().optional().describe(\"Filter results to a specific source\"),\n limit: z.number().optional().describe(\"Maximum number of results (default 10)\"),\n },\n async ({ query, source, limit }) => {\n if (!query.trim()) {\n return {\n content: [{ type: \"text\", text: \"Search query cannot be empty.\" }],\n isError: true,\n };\n }\n const results = searchIndex.search(query, { source, limit });\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n results.map((r) => ({\n source: r.source,\n path: r.path,\n title: r.title,\n score: r.score,\n })),\n null,\n 2\n ),\n },\n ],\n };\n }\n );\n\n return server;\n}\n","import { defineCommand, runMain, runCommand } from \"citty\";\nimport { fetchCommand } from \"./commands/fetch\";\nimport { addCommand } from \"./commands/add\";\nimport { updateCommand } from \"./commands/update\";\nimport { listCommand } from \"./commands/list\";\nimport { serveCommand } from \"./commands/serve\";\n\nconst subCommands: Record<string, any> = {\n add: addCommand,\n update: updateCommand,\n list: listCommand,\n serve: serveCommand,\n};\n\n// Check if first non-flag arg is a subcommand\nconst firstArg = process.argv[2];\nconst isSubCommand = firstArg && firstArg in subCommands;\n\nif (isSubCommand) {\n // Let citty handle subcommand routing\n const main = defineCommand({\n meta: {\n name: \"docs2ai\",\n version: \"0.1.0\",\n description: \"Convert documentation URLs into AI-ready Markdown files\",\n },\n subCommands,\n });\n runMain(main);\n} else if (firstArg && !firstArg.startsWith(\"-\") && firstArg !== \"--help\") {\n // Treat as a URL — run fetch command directly\n runCommand(fetchCommand, { rawArgs: process.argv.slice(2) });\n} else {\n // No args or --help — show usage\n const main = defineCommand({\n meta: {\n name: \"docs2ai\",\n version: \"0.1.0\",\n description: \"Convert documentation URLs into AI-ready Markdown files\",\n },\n subCommands,\n run() {\n console.log(\"Usage: docs2ai <url> [-o output.md] [--crawl]\");\n console.log(\" docs2ai add <url> [--name name] [--crawl]\");\n console.log(\" docs2ai update [--name name]\");\n console.log(\" docs2ai list\");\n console.log(\" docs2ai serve [-d dir]\");\n console.log(\"\\nRun `docs2ai --help` for full usage.\");\n },\n });\n runMain(main);\n}\n","import { defineCommand } from \"citty\";\nimport { dirname } from \"node:path\";\nimport consola from \"consola\";\nimport * as cheerio from \"cheerio\";\nimport { fetchPage, fetchWithBrowser } from \"../pipeline/fetcher\";\nimport { extract } from \"../pipeline/extractor\";\nimport { transform } from \"../pipeline/transformer\";\nimport { write, writePages } from \"../pipeline/writer\";\nimport { crawl } from \"../crawl/crawler\";\nimport { resolve } from \"../pipeline/resolver\";\nimport { getStrategy } from \"../platforms/registry\";\nimport { slugFromUrl } from \"../utils/url\";\nimport {\n buildSourceManifest,\n writeSourceManifest,\n updateRootManifest,\n} from \"../pipeline/manifest\";\nimport { extractSiteMeta } from \"../pipeline/meta-extractor\";\n\n/**\n * Determine whether crawl output should go to a directory (one file per page)\n * or a single stitched file.\n */\nfunction resolveOutputMode(\n output: string | undefined,\n shouldCrawl: boolean,\n name: string\n): { mode: \"single-file\" | \"directory\"; outputPath: string | undefined; outputDir: string } {\n if (!shouldCrawl) {\n return { mode: \"single-file\", outputPath: output, outputDir: \"\" };\n }\n\n // Crawl + explicit .md output → single file (backward compat)\n if (output && output.endsWith(\".md\")) {\n return { mode: \"single-file\", outputPath: output, outputDir: \"\" };\n }\n\n // Crawl + explicit directory path\n if (output) {\n const dir = output.endsWith(\"/\") ? output : output + \"/\";\n return { mode: \"directory\", outputPath: undefined, outputDir: dir };\n }\n\n // Crawl + no output → default directory\n return { mode: \"directory\", outputPath: undefined, outputDir: `.ai/docs/${name}/` };\n}\n\nexport const fetchCommand = defineCommand({\n meta: {\n name: \"fetch\",\n description: \"Fetch a documentation URL and convert to Markdown\",\n },\n args: {\n url: {\n type: \"positional\",\n description: \"Documentation URL to convert\",\n required: true,\n },\n output: {\n type: \"string\",\n alias: \"o\",\n description: \"Output file path or directory\",\n },\n name: {\n type: \"string\",\n description: \"Name for this source (auto-derived from hostname if omitted)\",\n },\n crawl: {\n type: \"boolean\",\n description: \"Follow sidebar/nav links\",\n default: false,\n },\n \"max-depth\": {\n type: \"string\",\n description: \"Maximum crawl depth\",\n default: \"2\",\n },\n },\n async run({ args }) {\n const url = args.url as string;\n const output = args.output as string | undefined;\n const shouldCrawl = args.crawl as boolean;\n const maxDepth = parseInt(args[\"max-depth\"] as string, 10);\n const name = (args.name as string) || slugFromUrl(url);\n\n const { mode, outputPath, outputDir } = resolveOutputMode(output, shouldCrawl, name);\n const silent = mode === \"single-file\" && !outputPath;\n\n if (shouldCrawl) {\n if (!silent) consola.start(`Crawling from ${url} (max depth: ${maxDepth})...`);\n\n // Fetch first page to resolve platform and get navLinkSelector\n const firstHtml = await fetchPage(url);\n const $ = cheerio.load(firstHtml);\n const platformId = resolve(url, $);\n const strategy = getStrategy(platformId);\n const navLinkSelector = strategy.navLinkSelector();\n\n const crawlResult = await crawl(url, {\n maxDepth,\n navLinkSelector,\n discoverUrls: strategy.discoverUrls?.bind(strategy),\n onPageFetched: (pageUrl, current, total) => {\n if (!silent) consola.info(`[${current}/${total}] ${pageUrl}`);\n },\n });\n\n const { pages, effectivePrefix } = crawlResult;\n if (!silent) consola.success(`Crawled ${pages.length} pages`);\n\n if (mode === \"directory\") {\n // Directory mode: one .md file per page + manifests\n const pageEntries = pages.map((page) => {\n const { content, title, platform } = extract(page.html, page.url);\n const md = transform(content);\n return { url: page.url, title, platform, markdown: md };\n });\n\n const firstPlatform = pageEntries[0]?.platform || \"generic\";\n const manifestPages = writePages(pageEntries, outputDir, effectivePrefix);\n\n const siteMeta = extractSiteMeta(firstHtml, url);\n const sourceManifest = buildSourceManifest(name, url, firstPlatform, manifestPages, siteMeta);\n writeSourceManifest(sourceManifest, outputDir);\n\n // Update root manifest in the parent directory\n const rootDir = dirname(outputDir.replace(/\\/$/, \"\"));\n updateRootManifest(rootDir, {\n name,\n path: name + \"/\",\n fetched_at: sourceManifest.fetched_at,\n display_name: siteMeta.displayName,\n description: siteMeta.description,\n icon_url: siteMeta.iconUrl,\n page_count: manifestPages.length,\n });\n\n consola.success(`Written ${pages.length} pages to ${outputDir}`);\n } else {\n // Single-file mode: stitch all pages together\n const sections: string[] = [];\n let firstTitle = \"\";\n let firstPlatform = \"\";\n\n for (const page of pages) {\n const { content, title, platform } = extract(page.html, page.url);\n if (!firstTitle) {\n firstTitle = title;\n firstPlatform = platform;\n }\n const md = transform(content);\n sections.push(`## ${title}\\n\\nSource: ${page.url}\\n\\n${md}`);\n }\n\n const markdown = sections.join(\"\\n\\n---\\n\\n\");\n\n write(markdown, outputPath, {\n sourceUrl: url,\n title: firstTitle,\n platform: firstPlatform,\n });\n\n if (!silent) consola.success(`Written to ${outputPath}`);\n }\n } else {\n if (!silent) consola.start(`Fetching ${url}...`);\n let html = await fetchPage(url);\n\n const { content, title, platform } = extract(html, url);\n\n // If content is suspiciously small, try Playwright\n if (content.trim().length < 200) {\n if (!silent) consola.warn(\"Content looks thin, retrying with browser...\");\n try {\n html = await fetchWithBrowser(url);\n const result = extract(html, url);\n const markdown = transform(result.content);\n write(markdown, outputPath, {\n sourceUrl: url,\n title: result.title || title,\n platform: result.platform,\n });\n if (!silent) consola.success(`Written to ${outputPath}`);\n return;\n } catch (err: any) {\n if (err?.code === \"ERR_PLAYWRIGHT_NOT_INSTALLED\") {\n consola.warn(\n \"This page may require a browser to render. Install Playwright:\\n\" +\n \" npm install -D playwright && npx playwright install chromium\"\n );\n } else {\n consola.warn(\"Browser fallback failed, using static content.\");\n }\n }\n }\n\n if (!silent) consola.success(`Extracted content (platform: ${platform})`);\n const markdown = transform(content);\n\n write(markdown, outputPath, {\n sourceUrl: url,\n title,\n platform,\n });\n\n if (!silent) consola.success(`Written to ${outputPath}`);\n }\n },\n});\n","import { ofetch } from \"ofetch\";\nimport consola from \"consola\";\n\n/** HTTP status codes that warrant a Playwright retry */\nconst BROWSER_RETRY_CODES = new Set([403, 406, 429]);\n\n/** Patterns that indicate a bot-protection challenge page */\nconst CHALLENGE_PATTERNS = [\n \"verify you are human\",\n \"just a moment\",\n \"checking your browser\",\n \"attention required\",\n \"enable javascript and cookies\",\n];\n\n/**\n * Detect if fetched HTML is a bot-protection challenge page\n * rather than real content.\n */\nfunction isChallengeContent(html: string): boolean {\n const lower = html.toLowerCase();\n return CHALLENGE_PATTERNS.some((p) => lower.includes(p));\n}\n\n/**\n * Fetch the raw HTML of a documentation page.\n * Uses static fetch by default, falls back to Playwright on blocked responses.\n */\nexport async function fetchPage(url: string): Promise<string> {\n try {\n return await ofetch(url, { responseType: \"text\" });\n } catch (err: any) {\n const status = err?.response?.status ?? err?.statusCode;\n if (status && BROWSER_RETRY_CODES.has(status)) {\n consola.warn(\n `Static fetch returned ${status}, retrying with browser...`\n );\n return fetchWithBrowser(url);\n }\n throw err;\n }\n}\n\n/**\n * Fetch a page using Playwright for JS-rendered sites.\n * Tries headless first, falls back to non-headless if a bot challenge is detected.\n * Playwright is an optional dependency — throws a typed error if not installed.\n */\nexport async function fetchWithBrowser(url: string): Promise<string> {\n const playwright = await loadPlaywright();\n\n // Try headless first\n let html = await launchAndFetch(playwright, url, true);\n\n // If we got a challenge page, retry with visible browser\n if (isChallengeContent(html)) {\n consola.warn(\"Bot protection detected, retrying with visible browser...\");\n html = await launchAndFetch(playwright, url, false);\n }\n\n return html;\n}\n\nasync function loadPlaywright() {\n try {\n return await import(\"playwright\");\n } catch {\n const err = new Error(\n \"Playwright is not installed. Run:\\n npm install -D playwright && npx playwright install chromium\"\n );\n (err as any).code = \"ERR_PLAYWRIGHT_NOT_INSTALLED\";\n throw err;\n }\n}\n\nasync function launchAndFetch(\n playwright: typeof import(\"playwright\"),\n url: string,\n headless: boolean\n): Promise<string> {\n const browser = await playwright.chromium.launch({ headless });\n try {\n const page = await browser.newPage();\n await page.goto(url, { waitUntil: \"domcontentloaded\", timeout: 30000 });\n // Wait for JS-rendered content to settle\n await page.waitForTimeout(2000);\n return await page.content();\n } finally {\n await browser.close();\n }\n}\n","import * as cheerio from \"cheerio\";\nimport { Readability } from \"@mozilla/readability\";\nimport { parseHTML } from \"linkedom\";\nimport { resolve } from \"./resolver\";\nimport { getStrategy } from \"../platforms/registry\";\nimport type { PlatformId } from \"../platforms/base\";\n\nexport interface ExtractResult {\n content: string;\n title: string;\n platform: PlatformId;\n}\n\n/**\n * Extract meaningful content from raw HTML.\n * Uses platform-specific selectors when available, falls back to Readability.\n */\nexport function extract(html: string, url: string): ExtractResult {\n const $ = cheerio.load(html);\n const platform = resolve(url, $);\n const strategy = getStrategy(platform);\n\n const title = extractTitle($);\n\n // Non-generic platforms: use selector-based extraction first\n if (platform !== \"generic\") {\n for (const sel of strategy.removeSelectors()) {\n $(sel).remove();\n }\n\n const contentEl = $(strategy.contentSelector()).first();\n const selectorContent = contentEl.html();\n\n if (selectorContent && selectorContent.trim().length >= 100) {\n return { content: selectorContent, title, platform };\n }\n // Fall through to Readability if selector extraction yields too little\n }\n\n // Generic / fallback: Readability extraction\n let article: ReturnType<Readability[\"parse\"]> = null;\n try {\n const { document } = parseHTML(html);\n const reader = new Readability(document as any);\n article = reader.parse();\n } catch {\n // Readability can throw on empty/malformed HTML — fall through to body/raw\n }\n\n const content = article?.content || $(\"body\").html() || html;\n\n return {\n content,\n title: title || article?.title || \"\",\n platform,\n };\n}\n\n/**\n * Extract page title from common sources.\n */\nfunction extractTitle($: cheerio.CheerioAPI): string {\n const h1 = $(\"h1\").first().text().trim();\n if (h1) return h1;\n\n const ogTitle = $('meta[property=\"og:title\"]').attr(\"content\")?.trim();\n if (ogTitle) return ogTitle;\n\n return $(\"title\").text().trim();\n}\n","import * as cheerio from \"cheerio\";\nimport type { CheerioAPI } from \"cheerio\";\nimport type { PlatformStrategy } from \"./base\";\n\nexport const mintlify: PlatformStrategy = {\n id: \"mintlify\",\n\n detect(url: string, $: CheerioAPI): boolean {\n if ($('meta[name=\"generator\"][content*=\"Mintlify\"]').length > 0) return true;\n if ($(\"script[src*='mintlify']\").length > 0) return true;\n if ($(\"[data-mintlify]\").length > 0) return true;\n return false;\n },\n\n contentSelector(): string {\n return \"article, main\";\n },\n\n removeSelectors(): string[] {\n return [\n \"nav\",\n \"header\",\n \"footer\",\n \"[role='navigation']\",\n \".sidebar\",\n \"[class*='sidebar']\",\n \"[class*='cookie']\",\n \"[class*='banner']\",\n \"script\",\n \"style\",\n ];\n },\n\n navLinkSelector(): string | null {\n return \"nav a[href], .sidebar a[href], [class*='sidebar'] a[href]\";\n },\n\n discoverUrls(html: string, baseUrl: string): string[] {\n const $ = cheerio.load(html);\n const paths = new Set<string>();\n\n // Mintlify uses Next.js — sidebar nav is in __next_f script data, not <a> tags.\n // The data contains escaped JSON like \\\"href\\\":\\\"/api-reference/checkouts/create\\\"\n $(\"script\").each((_, el) => {\n const text = $(el).html() || \"\";\n // Match escaped JSON paths: \\\"href\\\":\\\"/some-path\\\"\n const escaped = /\\\\?\"href\\\\?\"\\s*:\\s*\\\\?\"(\\/[a-z0-9][a-z0-9\\/-]*)\\\\?\"/g;\n let match = escaped.exec(text);\n while (match !== null) {\n paths.add(match[1]);\n match = escaped.exec(text);\n }\n });\n\n // Resolve to absolute URLs\n const origin = new URL(baseUrl).origin;\n // Determine docs base path (e.g. /docs from /docs/api-reference/intro)\n const basePath = new URL(baseUrl).pathname.split(\"/\").slice(0, 2).join(\"/\");\n\n return [...paths].map((p) => {\n if (p.startsWith(basePath)) {\n return origin + p;\n }\n return origin + basePath + p;\n });\n },\n};\n","import type { CheerioAPI } from \"cheerio\";\nimport type { PlatformStrategy } from \"./base\";\n\nexport const docusaurus: PlatformStrategy = {\n id: \"docusaurus\",\n\n detect(url: string, $: CheerioAPI): boolean {\n if ($('meta[name=\"generator\"][content*=\"Docusaurus\"]').length > 0)\n return true;\n if ($(\".theme-doc-sidebar-container\").length > 0) return true;\n if ($('meta[name=\"docusaurus_locale\"]').length > 0) return true;\n return false;\n },\n\n contentSelector(): string {\n return \"article, [role='main'], .theme-doc-markdown\";\n },\n\n removeSelectors(): string[] {\n return [\n \".navbar\",\n \"footer\",\n \".theme-doc-toc-desktop\",\n \".theme-doc-sidebar-container\",\n \".pagination-nav\",\n \".theme-doc-breadcrumbs\",\n \"nav\",\n \"script\",\n \"style\",\n ];\n },\n\n navLinkSelector(): string | null {\n return \".menu__link[href]\";\n },\n};\n","import type { CheerioAPI } from \"cheerio\";\nimport type { PlatformStrategy } from \"./base\";\n\nexport const readme: PlatformStrategy = {\n id: \"readme\",\n\n detect(url: string, $: CheerioAPI): boolean {\n let rmClassCount = 0;\n $(\"[class]\").each((_, el) => {\n const cls = $(el).attr(\"class\") || \"\";\n if (/\\brm-/.test(cls)) rmClassCount++;\n });\n if (rmClassCount > 2) return true;\n if ($(\".rm-Article\").length > 0) return true;\n if ($(\".rm-Markdown\").length > 0) return true;\n return false;\n },\n\n contentSelector(): string {\n return \".markdown-body, .rm-Article, .rm-Markdown\";\n },\n\n removeSelectors(): string[] {\n return [\n \"nav\",\n \"header\",\n \"footer\",\n \".rm-Sidebar\",\n \".rm-TableOfContents\",\n \"[class*='cookie']\",\n \"script\",\n \"style\",\n ];\n },\n\n navLinkSelector(): string | null {\n return \".rm-Sidebar a[href]\";\n },\n};\n","import type { CheerioAPI } from \"cheerio\";\nimport type { PlatformStrategy } from \"./base\";\n\nexport const gitbook: PlatformStrategy = {\n id: \"gitbook\",\n\n detect(url: string, $: CheerioAPI): boolean {\n if ($('meta[name=\"generator\"][content*=\"GitBook\"]').length > 0) return true;\n try {\n const parsed = new URL(url);\n if (parsed.hostname.endsWith(\".gitbook.io\")) return true;\n } catch {\n // invalid URL, skip host check\n }\n if ($('[data-testid=\"page.contentEditor\"]').length > 0) return true;\n return false;\n },\n\n contentSelector(): string {\n return '[data-testid=\"page.contentEditor\"], main, article';\n },\n\n removeSelectors(): string[] {\n return [\n \"nav\",\n \"header\",\n \"footer\",\n \"[class*='sidebar']\",\n \"[class*='toc']\",\n \"[class*='cookie']\",\n \"script\",\n \"style\",\n ];\n },\n\n navLinkSelector(): string | null {\n return \"nav a[href], aside a[href]\";\n },\n};\n","import type { CheerioAPI } from \"cheerio\";\nimport type { PlatformStrategy } from \"./base\";\n\nexport const generic: PlatformStrategy = {\n id: \"generic\",\n\n detect(_url: string, _$: CheerioAPI): boolean {\n return true;\n },\n\n contentSelector(): string {\n return \"article, main, [role='main'], .content\";\n },\n\n removeSelectors(): string[] {\n return [\n \"nav\",\n \"header\",\n \"footer\",\n \"[role='navigation']\",\n \"[class*='sidebar']\",\n \"[class*='cookie']\",\n \"[class*='banner']\",\n \"script\",\n \"style\",\n \"noscript\",\n ];\n },\n\n navLinkSelector(): string | null {\n return null;\n },\n};\n","import type { PlatformId, PlatformStrategy } from \"./base\";\nimport { mintlify } from \"./mintlify\";\nimport { docusaurus } from \"./docusaurus\";\nimport { readme } from \"./readme\";\nimport { gitbook } from \"./gitbook\";\nimport { generic } from \"./generic\";\n\n/** Ordered list of platform strategies. Generic must be last (always matches). */\nexport const platformStrategies: PlatformStrategy[] = [\n mintlify,\n docusaurus,\n readme,\n gitbook,\n generic,\n];\n\n/** Get a strategy by its platform ID. */\nexport function getStrategy(id: PlatformId): PlatformStrategy {\n const strategy = platformStrategies.find((s) => s.id === id);\n if (!strategy) {\n throw new Error(`Unknown platform: ${id}`);\n }\n return strategy;\n}\n","import type { CheerioAPI } from \"cheerio\";\nimport type { PlatformId } from \"../platforms/base\";\nimport { platformStrategies } from \"../platforms/registry\";\n\n/**\n * Detect which documentation platform a page belongs to.\n * Tries platform-specific strategies in order, falls back to generic.\n */\nexport function resolve(url: string, $: CheerioAPI): PlatformId {\n for (const strategy of platformStrategies) {\n if (strategy.detect(url, $)) {\n return strategy.id;\n }\n }\n return \"generic\";\n}\n","import TurndownService from \"turndown\";\nimport { gfm } from \"turndown-plugin-gfm\";\n\n/**\n * Convert clean HTML to Markdown.\n */\nexport function transform(html: string): string {\n const td = new TurndownService({\n headingStyle: \"atx\",\n codeBlockStyle: \"fenced\",\n bulletListMarker: \"-\",\n });\n\n td.use(gfm);\n\n addCalloutRule(td);\n addTabbedContentRule(td);\n addCodeBlockLangRule(td);\n addHiddenElementRule(td);\n\n return td.turndown(html);\n}\n\nfunction isElement(node: TurndownService.Node): node is HTMLElement {\n return node.nodeType === 1;\n}\n\nfunction getAttr(node: TurndownService.Node, attr: string): string {\n if (isElement(node)) {\n return node.getAttribute(attr) || \"\";\n }\n return \"\";\n}\n\nfunction getTagName(node: TurndownService.Node): string {\n if (isElement(node)) {\n return node.tagName.toLowerCase();\n }\n return \"\";\n}\n\n/**\n * Convert callouts/admonitions to blockquotes.\n * Matches: aside, .admonition, .callout, .alert, [role=\"alert\"]\n */\nfunction addCalloutRule(td: TurndownService): void {\n td.addRule(\"callouts\", {\n filter(node) {\n if (!isElement(node)) return false;\n const tag = getTagName(node);\n if (tag === \"aside\") return true;\n const cls = getAttr(node, \"class\");\n if (\n /\\b(admonition|callout|alert|notice|warning|info|tip|note|caution|danger)\\b/i.test(\n cls\n )\n )\n return true;\n if (getAttr(node, \"role\") === \"alert\") return true;\n return false;\n },\n replacement(content, node) {\n const cls = getAttr(node, \"class\").toLowerCase();\n let type = \"Note\";\n if (/warning|caution/.test(cls)) type = \"Warning\";\n else if (/danger|error/.test(cls)) type = \"Danger\";\n else if (/tip|success/.test(cls)) type = \"Tip\";\n else if (/info/.test(cls)) type = \"Info\";\n\n const lines = content.trim().split(\"\\n\");\n const quoted = lines.map((line) => `> ${line}`).join(\"\\n\");\n return `\\n> **${type}**\\n${quoted}\\n\\n`;\n },\n });\n}\n\n/**\n * Convert tabbed content into labeled sections.\n * Matches: .tab-panel, .tabpanel, [role=\"tabpanel\"]\n */\nfunction addTabbedContentRule(td: TurndownService): void {\n td.addRule(\"tabbed-content\", {\n filter(node) {\n if (!isElement(node)) return false;\n const cls = getAttr(node, \"class\");\n if (/\\b(tab-panel|tabpanel|tabs__item)\\b/i.test(cls)) return true;\n if (getAttr(node, \"role\") === \"tabpanel\") return true;\n return false;\n },\n replacement(content, node) {\n const label =\n getAttr(node, \"aria-label\") ||\n getAttr(node, \"data-label\") ||\n getAttr(node, \"data-value\") ||\n \"\";\n if (label) {\n return `\\n**${label}**\\n\\n${content.trim()}\\n\\n`;\n }\n return `\\n${content.trim()}\\n\\n`;\n },\n });\n}\n\n/**\n * Ensure code blocks with data-language/data-lang produce proper fenced blocks.\n */\nfunction addCodeBlockLangRule(td: TurndownService): void {\n td.addRule(\"code-block-lang\", {\n filter(node) {\n if (!isElement(node)) return false;\n if (getTagName(node) !== \"pre\") return false;\n const codeEl = node.querySelector(\"code\");\n if (!codeEl) return false;\n const lang =\n getAttr(node, \"data-language\") ||\n getAttr(node, \"data-lang\") ||\n (codeEl.getAttribute(\"data-language\") || \"\") ||\n (codeEl.getAttribute(\"data-lang\") || \"\");\n return lang.length > 0;\n },\n replacement(_content, node) {\n if (!isElement(node)) return _content;\n const codeEl = node.querySelector(\"code\")!;\n const lang =\n getAttr(node, \"data-language\") ||\n getAttr(node, \"data-lang\") ||\n (codeEl.getAttribute(\"data-language\") || \"\") ||\n (codeEl.getAttribute(\"data-lang\") || \"\");\n const code = codeEl.textContent || \"\";\n return `\\n\\`\\`\\`${lang}\\n${code}\\n\\`\\`\\`\\n`;\n },\n });\n}\n\n/**\n * Remove hidden elements (display:none) except tab panels.\n */\nfunction addHiddenElementRule(td: TurndownService): void {\n td.addRule(\"hidden-elements\", {\n filter(node) {\n if (!isElement(node)) return false;\n const style = getAttr(node, \"style\");\n if (!/display\\s*:\\s*none/i.test(style)) return false;\n // Don't remove tab panels — they're hidden but contain valid content\n const cls = getAttr(node, \"class\");\n if (/\\b(tab-panel|tabpanel)\\b/i.test(cls)) return false;\n if (getAttr(node, \"role\") === \"tabpanel\") return false;\n return true;\n },\n replacement() {\n return \"\";\n },\n });\n}\n","import { writeFileSync, mkdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport { filePathForPage } from \"../utils/slug\";\n\nexport interface WriterOptions {\n sourceUrl: string;\n title: string;\n platform: string;\n}\n\n/**\n * Write Markdown with frontmatter to a file or stdout.\n */\nexport function write(\n markdown: string,\n outputPath: string | undefined,\n options: WriterOptions\n): void {\n const content = matter.stringify(markdown, {\n source: options.sourceUrl,\n fetched_at: new Date().toISOString(),\n platform: options.platform,\n title: options.title,\n docs2ai_version: \"0.1.0\",\n });\n\n if (outputPath) {\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, content, \"utf-8\");\n } else {\n process.stdout.write(content);\n }\n}\n\n/**\n * Write a single page's Markdown with frontmatter to a file path (always writes to disk).\n */\nexport function writePage(\n markdown: string,\n filePath: string,\n options: WriterOptions\n): void {\n const content = matter.stringify(markdown, {\n source: options.sourceUrl,\n fetched_at: new Date().toISOString(),\n platform: options.platform,\n title: options.title,\n docs2ai_version: \"0.1.0\",\n });\n\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, content, \"utf-8\");\n}\n\nexport interface PageEntry {\n url: string;\n title: string;\n platform: string;\n markdown: string;\n}\n\n/**\n * Write multiple crawled pages to a directory, one .md file per page.\n * Returns manifest page entries (title + relative path) for each written page.\n */\nexport function writePages(\n pages: PageEntry[],\n outputDir: string,\n basePrefix: string\n): { title: string; path: string }[] {\n const usedPaths = new Set<string>();\n const entries: { title: string; path: string }[] = [];\n\n for (const page of pages) {\n let relPath = filePathForPage(page.url, basePrefix);\n\n // Handle slug collisions by appending a numeric suffix\n if (usedPaths.has(relPath)) {\n const base = relPath.replace(/\\.md$/, \"\");\n let i = 2;\n while (usedPaths.has(`${base}-${i}.md`)) i++;\n relPath = `${base}-${i}.md`;\n }\n usedPaths.add(relPath);\n\n const filePath = join(outputDir, relPath);\n writePage(page.markdown, filePath, {\n sourceUrl: page.url,\n title: page.title,\n platform: page.platform,\n });\n\n entries.push({ title: page.title, path: relPath });\n }\n\n return entries;\n}\n","/**\n * Derive a filename slug from a URL's pathname.\n * Strips leading/trailing slashes and returns the last segment.\n */\nexport function slugFromPathname(url: string): string {\n const parsed = new URL(url);\n const pathname = parsed.pathname.replace(/\\/+$/, \"\");\n if (!pathname || pathname === \"/\") return \"index\";\n const segments = pathname.split(\"/\").filter(Boolean);\n return segments[segments.length - 1];\n}\n\n/**\n * Compute a relative file path for a crawled page based on its URL and a base prefix.\n * Strips the basePrefix from the pathname, intermediate segments become subdirectories,\n * and the last segment becomes the filename with .md extension.\n *\n * @example filePathForPage(\"https://x.com/docs/guides/auth\", \"/docs/\") → \"guides/auth.md\"\n * @example filePathForPage(\"https://x.com/docs/getting-started\", \"/docs/\") → \"getting-started.md\"\n * @example filePathForPage(\"https://x.com/docs/\", \"/docs/\") → \"index.md\"\n */\nexport function filePathForPage(pageUrl: string, basePrefix: string): string {\n const parsed = new URL(pageUrl);\n let pathname = parsed.pathname.replace(/\\/+$/, \"\");\n\n // Strip the base prefix\n const normalizedPrefix = basePrefix.replace(/\\/+$/, \"\");\n if (pathname.startsWith(normalizedPrefix)) {\n pathname = pathname.slice(normalizedPrefix.length);\n }\n\n // Remove leading slash\n pathname = pathname.replace(/^\\/+/, \"\");\n\n if (!pathname) return \"index.md\";\n\n return pathname + \".md\";\n}\n","import * as cheerio from \"cheerio\";\nimport { fetchPage } from \"../pipeline/fetcher\";\nimport {\n getCrawlPrefix,\n computeCommonPrefix,\n isInBounds,\n normalizeUrl,\n} from \"./boundary\";\n\nexport interface CrawledPage {\n url: string;\n html: string;\n}\n\nexport interface CrawlResult {\n pages: CrawledPage[];\n effectivePrefix: string;\n}\n\nexport interface CrawlOptions {\n maxDepth: number;\n navLinkSelector?: string | null;\n /** Custom URL discovery for SPA-rendered sidebars (overrides navLinkSelector) */\n discoverUrls?: (html: string, baseUrl: string) => string[];\n onPageFetched?: (url: string, current: number, total: number) => void;\n}\n\n/**\n * Crawl documentation pages starting from a URL.\n * Follows in-bounds links via BFS up to maxDepth.\n */\nexport async function crawl(\n startUrl: string,\n options: CrawlOptions\n): Promise<CrawlResult> {\n const { origin } = getCrawlPrefix(startUrl);\n let { pathPrefix } = getCrawlPrefix(startUrl);\n const visited = new Set<string>();\n const results: CrawledPage[] = [];\n let isFirstPage = true;\n\n // BFS queue: [url, depth]\n const queue: [string, number][] = [[startUrl, 0]];\n visited.add(normalizeUrl(startUrl));\n\n while (queue.length > 0) {\n const [url, depth] = queue.shift()!;\n\n let html: string;\n try {\n html = await fetchPage(url);\n } catch {\n options.onPageFetched?.(url, results.length, results.length + queue.length);\n continue;\n }\n results.push({ url, html });\n options.onPageFetched?.(url, results.length, results.length + queue.length);\n\n if (depth < options.maxDepth) {\n // On the first page, discover all same-origin nav links to widen the boundary\n if (isFirstPage) {\n const allNavUrls = options.discoverUrls\n ? discoverSameOriginCustom(html, url, origin, options.discoverUrls)\n : discoverSameOrigin(html, url, origin, options.navLinkSelector);\n if (allNavUrls.length > 0) {\n pathPrefix = computeCommonPrefix(startUrl, allNavUrls);\n }\n isFirstPage = false;\n }\n\n const links = options.discoverUrls\n ? discoverLinksCustom(html, url, origin, pathPrefix, options.discoverUrls)\n : discoverLinks(html, url, origin, pathPrefix, options.navLinkSelector);\n for (const link of links) {\n const normalized = normalizeUrl(link);\n if (!visited.has(normalized)) {\n visited.add(normalized);\n queue.push([link, depth + 1]);\n }\n }\n }\n\n // Politeness delay between requests\n if (queue.length > 0) {\n await delay(200);\n }\n }\n\n return { pages: results, effectivePrefix: pathPrefix };\n}\n\n/**\n * Extract all in-bounds links from a page's HTML.\n * When navLinkSelector is provided, only links matching that selector are used.\n */\nfunction discoverLinks(\n html: string,\n baseUrl: string,\n origin: string,\n pathPrefix: string,\n navLinkSelector?: string | null\n): string[] {\n const $ = cheerio.load(html);\n const links: string[] = [];\n const selector = navLinkSelector || \"a[href]\";\n\n $(selector).each((_, el) => {\n const href = $(el).attr(\"href\");\n if (!href) return;\n\n try {\n const resolved = new URL(href, baseUrl).href;\n if (isInBounds(resolved, origin, pathPrefix)) {\n links.push(resolved);\n }\n } catch {\n // Invalid URL, skip\n }\n });\n\n return [...new Set(links)];\n}\n\n/**\n * Discover all same-origin links from nav (no path prefix filter).\n * Used on the first page to compute the crawl boundary from nav structure.\n */\nfunction discoverSameOrigin(\n html: string,\n baseUrl: string,\n origin: string,\n navLinkSelector?: string | null\n): string[] {\n const $ = cheerio.load(html);\n const links: string[] = [];\n const selector = navLinkSelector || \"a[href]\";\n\n $(selector).each((_, el) => {\n const href = $(el).attr(\"href\");\n if (!href) return;\n try {\n const resolved = new URL(href, baseUrl).href;\n if (new URL(resolved).origin === origin) {\n links.push(resolved);\n }\n } catch {\n // Invalid URL, skip\n }\n });\n\n return [...new Set(links)];\n}\n\n/**\n * Discover all same-origin links via custom discovery (no path prefix filter).\n */\nfunction discoverSameOriginCustom(\n html: string,\n baseUrl: string,\n origin: string,\n discoverUrls: (html: string, baseUrl: string) => string[]\n): string[] {\n const urls = discoverUrls(html, baseUrl);\n return [\n ...new Set(\n urls.filter((u) => {\n try {\n return new URL(u).origin === origin;\n } catch {\n return false;\n }\n })\n ),\n ];\n}\n\n/**\n * Extract in-bounds links using a custom discovery function from the platform strategy.\n */\nfunction discoverLinksCustom(\n html: string,\n baseUrl: string,\n origin: string,\n pathPrefix: string,\n discoverUrls: (html: string, baseUrl: string) => string[]\n): string[] {\n const urls = discoverUrls(html, baseUrl);\n return [...new Set(urls.filter((u) => isInBounds(u, origin, pathPrefix)))];\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Validate whether a string is a valid URL.\n */\nexport function isValidUrl(input: string): boolean {\n try {\n new URL(input);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Normalize a URL for deduplication: strip hash, query, trailing slash.\n */\nexport function normalizeUrl(url: string): string {\n const parsed = new URL(url);\n parsed.hash = \"\";\n parsed.search = \"\";\n return parsed.href.replace(/\\/$/, \"\");\n}\n\n/**\n * Derive a short name/slug from a URL's hostname.\n */\nexport function slugFromUrl(url: string): string {\n try {\n const parsed = new URL(url);\n return parsed.hostname.replace(/\\./g, \"-\").replace(/^www-/, \"\");\n } catch {\n return \"source\";\n }\n}\n","import { normalizeUrl } from \"../utils/url\";\n\nexport { normalizeUrl };\n\n/**\n * Determine the crawl boundary from a starting URL.\n * Links are in-bounds if they share the same origin and path prefix.\n */\nexport function getCrawlPrefix(url: string): {\n origin: string;\n pathPrefix: string;\n} {\n const parsed = new URL(url);\n const pathParts = parsed.pathname.split(\"/\");\n // Remove the last segment (the current page slug)\n pathParts.pop();\n const pathPrefix = pathParts.join(\"/\") + \"/\";\n return { origin: parsed.origin, pathPrefix };\n}\n\n/**\n * Compute the longest common path prefix between a start URL and discovered nav URLs.\n * Used to widen the crawl boundary when sidebar links span multiple sections.\n */\nexport function computeCommonPrefix(\n startUrl: string,\n navUrls: string[]\n): string {\n const startParts = new URL(startUrl).pathname.split(\"/\").filter(Boolean);\n const parts = [...startParts];\n\n for (const url of navUrls) {\n const urlParts = new URL(url).pathname.split(\"/\").filter(Boolean);\n let i = 0;\n while (i < parts.length && i < urlParts.length && parts[i] === urlParts[i]) {\n i++;\n }\n parts.length = i;\n }\n\n return \"/\" + (parts.length > 0 ? parts.join(\"/\") + \"/\" : \"\");\n}\n\n/**\n * Check whether a candidate URL falls within the crawl boundary.\n */\nexport function isInBounds(\n candidateUrl: string,\n origin: string,\n pathPrefix: string\n): boolean {\n try {\n const parsed = new URL(candidateUrl);\n return parsed.origin === origin && parsed.pathname.startsWith(pathPrefix);\n } catch {\n return false;\n }\n}\n","import { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport type { SiteMeta } from \"./meta-extractor\";\n\n/** Manifest for a single documentation source (written as _index.json). */\nexport interface SourceManifest {\n name: string;\n url: string;\n platform: string;\n fetched_at: string;\n pages: { title: string; path: string }[];\n display_name?: string;\n description?: string;\n icon_url?: string | null;\n og_image?: string | null;\n language?: string | null;\n page_count?: number;\n}\n\n/** Entry in the root manifest's sources array. */\nexport interface RootManifestEntry {\n name: string;\n path: string;\n fetched_at: string;\n display_name?: string;\n description?: string;\n icon_url?: string | null;\n page_count?: number;\n}\n\n/** Root manifest listing all sources (written as manifest.json). */\nexport interface RootManifest {\n sources: RootManifestEntry[];\n}\n\n/**\n * Build a source manifest object.\n * When siteMeta is provided, its fields are included in the manifest.\n */\nexport function buildSourceManifest(\n name: string,\n url: string,\n platform: string,\n pages: { title: string; path: string }[],\n siteMeta?: SiteMeta\n): SourceManifest {\n const manifest: SourceManifest = {\n name,\n url,\n platform,\n fetched_at: new Date().toISOString(),\n pages,\n };\n\n if (siteMeta) {\n manifest.display_name = siteMeta.displayName;\n manifest.description = siteMeta.description;\n manifest.icon_url = siteMeta.iconUrl;\n manifest.og_image = siteMeta.ogImage;\n manifest.language = siteMeta.language;\n manifest.page_count = pages.length;\n }\n\n return manifest;\n}\n\n/**\n * Write a source manifest (_index.json) to a directory.\n */\nexport function writeSourceManifest(\n manifest: SourceManifest,\n outputDir: string\n): void {\n mkdirSync(outputDir, { recursive: true });\n writeFileSync(\n join(outputDir, \"_index.json\"),\n JSON.stringify(manifest, null, 2) + \"\\n\",\n \"utf-8\"\n );\n}\n\n/**\n * Load the root manifest (manifest.json) from a directory.\n * Returns an empty manifest if the file doesn't exist.\n */\nexport function loadRootManifest(rootDir: string): RootManifest {\n try {\n const raw = readFileSync(join(rootDir, \"manifest.json\"), \"utf-8\");\n return JSON.parse(raw) as RootManifest;\n } catch {\n return { sources: [] };\n }\n}\n\n/**\n * Upsert a source entry in the root manifest and write it to disk.\n */\nexport function updateRootManifest(\n rootDir: string,\n entry: RootManifestEntry\n): void {\n const manifest = loadRootManifest(rootDir);\n const idx = manifest.sources.findIndex((s) => s.name === entry.name);\n if (idx >= 0) {\n manifest.sources[idx] = entry;\n } else {\n manifest.sources.push(entry);\n }\n mkdirSync(rootDir, { recursive: true });\n writeFileSync(\n join(rootDir, \"manifest.json\"),\n JSON.stringify(manifest, null, 2) + \"\\n\",\n \"utf-8\"\n );\n}\n","import * as cheerio from \"cheerio\";\n\n/** Site-level metadata extracted from a page's <head>. */\nexport interface SiteMeta {\n displayName: string;\n description: string;\n iconUrl: string | null;\n ogImage: string | null;\n language: string | null;\n}\n\n/**\n * Extract site metadata from an HTML page's <head> element.\n * Pure function — no side effects or network calls.\n */\nexport function extractSiteMeta(html: string, url: string): SiteMeta {\n const $ = cheerio.load(html);\n const origin = new URL(url).origin;\n\n return {\n displayName: extractDisplayName($, url),\n description: extractDescription($),\n iconUrl: extractIconUrl($, origin),\n ogImage: extractOgImage($, origin),\n language: extractLanguage($),\n };\n}\n\nfunction nonEmpty(value: string | undefined): string | undefined {\n return value?.trim() || undefined;\n}\n\nfunction extractDisplayName($: cheerio.CheerioAPI, url: string): string {\n const ogSiteName = nonEmpty($('meta[property=\"og:site_name\"]').attr(\"content\"));\n if (ogSiteName) return ogSiteName;\n\n const appName = nonEmpty($('meta[name=\"application-name\"]').attr(\"content\"));\n if (appName) return appName;\n\n const title = nonEmpty($(\"title\").text());\n if (title) {\n const parts = title.split(/\\s[-|—]\\s/);\n return parts[0].trim();\n }\n\n return new URL(url).hostname;\n}\n\nfunction extractDescription($: cheerio.CheerioAPI): string {\n const ogDesc = nonEmpty($('meta[property=\"og:description\"]').attr(\"content\"));\n if (ogDesc) return ogDesc;\n\n const metaDesc = nonEmpty($('meta[name=\"description\"]').attr(\"content\"));\n if (metaDesc) return metaDesc;\n\n return \"\";\n}\n\nfunction extractIconUrl($: cheerio.CheerioAPI, origin: string): string | null {\n const selectors = [\n 'link[rel=\"apple-touch-icon\"]',\n 'link[rel=\"icon\"][type=\"image/svg+xml\"]',\n 'link[rel=\"icon\"]',\n 'link[rel=\"shortcut icon\"]',\n ];\n\n for (const selector of selectors) {\n const href = nonEmpty($(selector).attr(\"href\"));\n if (href) return resolveUrl(href, origin);\n }\n\n return `${origin}/favicon.ico`;\n}\n\nfunction extractOgImage($: cheerio.CheerioAPI, origin: string): string | null {\n const ogImage = nonEmpty($('meta[property=\"og:image\"]').attr(\"content\"));\n if (ogImage) return resolveUrl(ogImage, origin);\n\n return null;\n}\n\nfunction extractLanguage($: cheerio.CheerioAPI): string | null {\n const htmlLang = nonEmpty($(\"html\").attr(\"lang\"));\n if (htmlLang) return htmlLang;\n\n const ogLocale = nonEmpty($('meta[property=\"og:locale\"]').attr(\"content\"));\n if (ogLocale) return ogLocale;\n\n return null;\n}\n\nfunction resolveUrl(href: string, origin: string): string {\n if (href.startsWith(\"http://\") || href.startsWith(\"https://\")) return href;\n if (href.startsWith(\"//\")) return `https:${href}`;\n if (href.startsWith(\"/\")) return `${origin}${href}`;\n return `${origin}/${href}`;\n}\n","import { defineCommand } from \"citty\";\nimport { join } from \"node:path\";\nimport consola from \"consola\";\nimport { loadConfig, saveConfig, addSource } from \"../config/manager\";\nimport type { Docs2aiConfig } from \"../config/schema\";\nimport { slugFromUrl } from \"../utils/url\";\n\nexport const addCommand = defineCommand({\n meta: {\n name: \"add\",\n description: \"Add a documentation source to .docs2ai.yaml\",\n },\n args: {\n url: {\n type: \"positional\",\n description: \"Documentation URL to add\",\n required: true,\n },\n name: {\n type: \"string\",\n description: \"Name for this source (auto-derived from hostname if omitted)\",\n },\n crawl: {\n type: \"boolean\",\n description: \"Enable crawl mode for this source\",\n default: false,\n },\n \"max-depth\": {\n type: \"string\",\n description: \"Maximum crawl depth\",\n default: \"2\",\n },\n output: {\n type: \"string\",\n alias: \"o\",\n description: \"Output filename or directory\",\n },\n },\n run({ args }) {\n const url = args.url as string;\n const shouldCrawl = args.crawl as boolean;\n const maxDepth = parseInt(args[\"max-depth\"] as string, 10);\n\n const name = (args.name as string) || slugFromUrl(url);\n const output =\n (args.output as string) || (shouldCrawl ? `${name}/` : `${name}.md`);\n\n const existing = loadConfig();\n let config: Docs2aiConfig;\n let configPath: string;\n\n if (existing) {\n config = existing.config;\n configPath = existing.configPath;\n } else {\n configPath = join(process.cwd(), \".docs2ai.yaml\");\n config = { version: 1, outputDir: \".ai/docs\", sources: [] };\n }\n\n addSource(config, { name, url, crawl: shouldCrawl, maxDepth, output });\n saveConfig(config, configPath);\n\n consola.success(`Added source \"${name}\" → ${url}`);\n consola.info(`Config: ${configPath}`);\n },\n});\n","import { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport yaml from \"js-yaml\";\nimport type { Docs2aiConfig, SourceConfig } from \"./schema\";\n\nconst CONFIG_FILENAME = \".docs2ai.yaml\";\n\n/**\n * Load the .docs2ai.yaml config file, searching up from cwd.\n * Returns null if no config file is found.\n */\nexport function loadConfig(startDir?: string): {\n config: Docs2aiConfig;\n configPath: string;\n} | null {\n const configPath = findConfigFile(startDir || process.cwd());\n if (!configPath) return null;\n\n const raw = readFileSync(configPath, \"utf-8\");\n const data = yaml.load(raw) as Record<string, any>;\n\n const config: Docs2aiConfig = {\n version: data.version ?? 1,\n outputDir: data.output_dir ?? \".ai/docs\",\n sources: (data.sources ?? []).map(snakeToCamelSource),\n };\n\n return { config, configPath };\n}\n\n/**\n * Save configuration to a .docs2ai.yaml file.\n */\nexport function saveConfig(config: Docs2aiConfig, configPath: string): void {\n const data = {\n version: config.version,\n output_dir: config.outputDir,\n sources: config.sources.map(camelToSnakeSource),\n };\n\n const content = yaml.dump(data, { lineWidth: -1 });\n writeFileSync(configPath, content, \"utf-8\");\n}\n\n/**\n * Add or update a source in the config (upsert by name).\n */\nexport function addSource(config: Docs2aiConfig, source: SourceConfig): void {\n const idx = config.sources.findIndex((s) => s.name === source.name);\n if (idx >= 0) {\n config.sources[idx] = source;\n } else {\n config.sources.push(source);\n }\n}\n\n/**\n * Walk up the directory tree looking for .docs2ai.yaml.\n */\nfunction findConfigFile(startDir: string): string | null {\n let dir = startDir;\n while (true) {\n const candidate = join(dir, CONFIG_FILENAME);\n if (existsSync(candidate)) return candidate;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nfunction snakeToCamelSource(s: Record<string, any>): SourceConfig {\n return {\n name: s.name ?? \"\",\n url: s.url ?? \"\",\n crawl: s.crawl ?? false,\n maxDepth: s.max_depth ?? 2,\n output: s.output ?? \"\",\n };\n}\n\nfunction camelToSnakeSource(\n s: SourceConfig\n): Record<string, string | number | boolean> {\n return {\n name: s.name,\n url: s.url,\n crawl: s.crawl,\n max_depth: s.maxDepth,\n output: s.output,\n };\n}\n","import { defineCommand } from \"citty\";\nimport { join, dirname } from \"node:path\";\nimport { mkdirSync } from \"node:fs\";\nimport * as cheerio from \"cheerio\";\nimport consola from \"consola\";\nimport { loadConfig } from \"../config/manager\";\nimport { fetchPage } from \"../pipeline/fetcher\";\nimport { extract } from \"../pipeline/extractor\";\nimport { transform } from \"../pipeline/transformer\";\nimport { write, writePages } from \"../pipeline/writer\";\nimport { crawl } from \"../crawl/crawler\";\nimport { resolve } from \"../pipeline/resolver\";\nimport { getStrategy } from \"../platforms/registry\";\nimport {\n buildSourceManifest,\n writeSourceManifest,\n updateRootManifest,\n} from \"../pipeline/manifest\";\nimport { extractSiteMeta } from \"../pipeline/meta-extractor\";\n\nexport const updateCommand = defineCommand({\n meta: {\n name: \"update\",\n description: \"Refresh configured documentation sources\",\n },\n args: {\n name: {\n type: \"string\",\n description: \"Update only the named source\",\n },\n },\n async run({ args }) {\n const result = loadConfig();\n if (!result) {\n consola.error(\"No .docs2ai.yaml found. Run `docs2ai add <url>` first.\");\n process.exit(1);\n }\n\n const { config, configPath } = result;\n const configDir = dirname(configPath);\n const filterName = args.name as string | undefined;\n\n const sources = filterName\n ? config.sources.filter((s) => s.name === filterName)\n : config.sources;\n\n if (sources.length === 0) {\n if (filterName) {\n consola.error(`Source \"${filterName}\" not found in config.`);\n } else {\n consola.error(\"No sources configured.\");\n }\n process.exit(1);\n }\n\n for (const source of sources) {\n const isDirectoryOutput = !source.output.endsWith(\".md\");\n\n consola.start(`Updating \"${source.name}\" from ${source.url}...`);\n\n if (source.crawl) {\n // Fetch first page to resolve platform and get link discovery\n const firstHtml = await fetchPage(source.url);\n const $ = cheerio.load(firstHtml);\n const platformId = resolve(source.url, $);\n const strategy = getStrategy(platformId);\n\n const crawlResult = await crawl(source.url, {\n maxDepth: source.maxDepth,\n navLinkSelector: strategy.navLinkSelector(),\n discoverUrls: strategy.discoverUrls?.bind(strategy),\n onPageFetched: (url, current, total) => {\n consola.info(` [${current}/${total}] ${url}`);\n },\n });\n\n const { pages, effectivePrefix } = crawlResult;\n\n if (isDirectoryOutput) {\n // Directory mode: one .md file per page + manifests\n const outputDir = join(configDir, config.outputDir, source.output);\n const pageEntries = pages.map((page) => {\n const { content, title, platform } = extract(page.html, page.url);\n const md = transform(content);\n return { url: page.url, title, platform, markdown: md };\n });\n\n const firstPlatform = pageEntries[0]?.platform || \"generic\";\n const manifestPages = writePages(pageEntries, outputDir, effectivePrefix);\n\n const siteMeta = extractSiteMeta(firstHtml, source.url);\n const sourceManifest = buildSourceManifest(\n source.name,\n source.url,\n firstPlatform,\n manifestPages,\n siteMeta\n );\n writeSourceManifest(sourceManifest, outputDir);\n\n const rootDir = join(configDir, config.outputDir);\n updateRootManifest(rootDir, {\n name: source.name,\n path: source.output,\n fetched_at: sourceManifest.fetched_at,\n display_name: siteMeta.displayName,\n description: siteMeta.description,\n icon_url: siteMeta.iconUrl,\n page_count: manifestPages.length,\n });\n\n consola.success(`Updated \"${source.name}\" → ${outputDir} (${pages.length} pages)`);\n } else {\n // Single-file mode: stitch all pages together\n const outputPath = join(configDir, config.outputDir, source.output);\n mkdirSync(dirname(outputPath), { recursive: true });\n\n const sections: string[] = [];\n let firstTitle = \"\";\n let firstPlatform = \"\";\n\n for (const page of pages) {\n const { content, title, platform } = extract(page.html, page.url);\n if (!firstTitle) {\n firstTitle = title;\n firstPlatform = platform;\n }\n const md = transform(content);\n sections.push(`## ${title}\\n\\nSource: ${page.url}\\n\\n${md}`);\n }\n\n const markdown = sections.join(\"\\n\\n---\\n\\n\");\n write(markdown, outputPath, {\n sourceUrl: source.url,\n title: firstTitle,\n platform: firstPlatform,\n });\n\n consola.success(`Updated \"${source.name}\" → ${outputPath}`);\n }\n } else {\n const outputPath = join(configDir, config.outputDir, source.output);\n mkdirSync(dirname(outputPath), { recursive: true });\n\n const html = await fetchPage(source.url);\n const { content, title, platform } = extract(html, source.url);\n const markdown = transform(content);\n write(markdown, outputPath, {\n sourceUrl: source.url,\n title,\n platform,\n });\n\n consola.success(`Updated \"${source.name}\" → ${outputPath}`);\n }\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport consola from \"consola\";\nimport { loadConfig } from \"../config/manager\";\n\nexport const listCommand = defineCommand({\n meta: {\n name: \"list\",\n description: \"List configured documentation sources\",\n },\n run() {\n const result = loadConfig();\n if (!result) {\n consola.info(\"No .docs2ai.yaml found. Run `docs2ai add <url>` to get started.\");\n return;\n }\n\n const { config, configPath } = result;\n consola.info(`Config: ${configPath}`);\n consola.info(`Output dir: ${config.outputDir}\\n`);\n\n if (config.sources.length === 0) {\n consola.info(\"No sources configured.\");\n return;\n }\n\n for (const source of config.sources) {\n const crawlInfo = source.crawl\n ? ` (crawl, depth: ${source.maxDepth})`\n : \"\";\n console.log(` ${source.name}${crawlInfo}`);\n console.log(` URL: ${source.url}`);\n console.log(` Output: ${source.output}`);\n console.log();\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { resolve } from \"node:path\";\n\nexport const serveCommand = defineCommand({\n meta: {\n name: \"serve\",\n description: \"Start an MCP server exposing documentation tools\",\n },\n args: {\n dir: {\n type: \"string\",\n alias: \"d\",\n description: \"Documentation directory to serve\",\n default: \".ai/docs/\",\n },\n },\n async run({ args }) {\n const docsDir = resolve(process.cwd(), args.dir as string);\n const { createMcpServer } = await import(\"../mcp/server\");\n const { StdioServerTransport } = await import(\n \"@modelcontextprotocol/sdk/server/stdio.js\"\n );\n const server = createMcpServer(docsDir);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n },\n});\n"],"mappings":"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","names":["readFileSync","join","matter","defineCommand","dirname","consola","cheerio","cheerio","cheerio","resolve","writeFileSync","mkdirSync","join","cheerio","consola","dirname","markdown","defineCommand","join","consola","readFileSync","writeFileSync","join","dirname","defineCommand","join","consola","defineCommand","join","dirname","mkdirSync","cheerio","consola","defineCommand","consola","dirname","join","mkdirSync","defineCommand","consola","defineCommand","consola","defineCommand","resolve","createMcpServer","defineCommand"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docs2ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Convert online documentation into AI-ready Markdown context files",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"@types/node": "^25.2.2",
|
|
67
67
|
"@types/turndown": "^5.0.6",
|
|
68
68
|
"eslint": "^10.0.0",
|
|
69
|
+
"playwright": "^1.58.2",
|
|
69
70
|
"prettier": "^3.8.1",
|
|
70
71
|
"tsup": "^8.5.1",
|
|
71
72
|
"tsx": "^4.21.0",
|