claude-local-docs 1.0.2 → 1.0.13
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/.claude-plugin/marketplace.json +25 -5
- package/.claude-plugin/plugin.json +25 -4
- package/.mcp.json +9 -3
- package/README.md +162 -78
- package/commands/fetch-docs.md +146 -33
- package/dist/discovery.d.ts +46 -0
- package/dist/discovery.js +357 -0
- package/dist/discovery.js.map +1 -0
- package/dist/fetcher.d.ts +6 -1
- package/dist/fetcher.js +8 -5
- package/dist/fetcher.js.map +1 -1
- package/dist/index.js +85 -5
- package/dist/index.js.map +1 -1
- package/dist/indexer.d.ts +4 -4
- package/dist/indexer.js +95 -37
- package/dist/indexer.js.map +1 -1
- package/dist/integration.test.d.ts +8 -0
- package/dist/integration.test.js +114 -0
- package/dist/integration.test.js.map +1 -0
- package/dist/reranker.d.ts +2 -4
- package/dist/reranker.js +14 -42
- package/dist/reranker.js.map +1 -1
- package/dist/search.js +5 -4
- package/dist/search.js.map +1 -1
- package/docker-compose.nvidia.yml +14 -0
- package/docker-compose.yml +20 -0
- package/hooks/hooks.json +16 -0
- package/package.json +14 -3
- package/scripts/ensure-tei.sh +71 -0
- package/start-tei.sh +239 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-contained doc discovery: npm registry lookup, llms.txt probing,
|
|
3
|
+
* index detection & expansion, HTML-to-markdown conversion.
|
|
4
|
+
*/
|
|
5
|
+
interface NpmInfo {
|
|
6
|
+
name: string;
|
|
7
|
+
version: string;
|
|
8
|
+
homepage?: string;
|
|
9
|
+
repoUrl?: string;
|
|
10
|
+
repoOrg?: string;
|
|
11
|
+
repoName?: string;
|
|
12
|
+
/** URL from package.json "llms" field (Colin Hacks convention) */
|
|
13
|
+
llmsUrl?: string;
|
|
14
|
+
/** URL from package.json "llmsFull" field (Colin Hacks convention) */
|
|
15
|
+
llmsFullUrl?: string;
|
|
16
|
+
}
|
|
17
|
+
interface IndexDetection {
|
|
18
|
+
isIndex: boolean;
|
|
19
|
+
links: {
|
|
20
|
+
text: string;
|
|
21
|
+
url: string;
|
|
22
|
+
}[];
|
|
23
|
+
}
|
|
24
|
+
export interface DiscoveryResult {
|
|
25
|
+
url: string;
|
|
26
|
+
content: string;
|
|
27
|
+
byteLength: number;
|
|
28
|
+
source: "npm-llms-field" | "llms-full.txt" | "llms.txt" | "llms.txt-index" | "homepage-html" | "github-raw" | "readme";
|
|
29
|
+
expandedUrls?: string[];
|
|
30
|
+
failedUrls?: string[];
|
|
31
|
+
warning?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function queryNpmRegistry(library: string): Promise<NpmInfo>;
|
|
34
|
+
export declare function generateCandidateUrls(info: NpmInfo): string[];
|
|
35
|
+
export declare function detectIndex(content: string, url: string): IndexDetection;
|
|
36
|
+
export declare function expandIndex(links: {
|
|
37
|
+
text: string;
|
|
38
|
+
url: string;
|
|
39
|
+
}[], baseUrl: string): Promise<{
|
|
40
|
+
content: string;
|
|
41
|
+
expandedUrls: string[];
|
|
42
|
+
failedUrls: string[];
|
|
43
|
+
}>;
|
|
44
|
+
export declare function htmlToMarkdown(html: string): string;
|
|
45
|
+
export declare function resolveDocsUrl(library: string): Promise<DiscoveryResult>;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-contained doc discovery: npm registry lookup, llms.txt probing,
|
|
3
|
+
* index detection & expansion, HTML-to-markdown conversion.
|
|
4
|
+
*/
|
|
5
|
+
import TurndownService from "turndown";
|
|
6
|
+
import { gfm } from "turndown-plugin-gfm";
|
|
7
|
+
import { fetchDocContent } from "./fetcher.js";
|
|
8
|
+
// ── npm registry ───────────────────────────────────────────────────────
|
|
9
|
+
export async function queryNpmRegistry(library) {
|
|
10
|
+
const url = `https://registry.npmjs.org/${encodeURIComponent(library)}/latest`;
|
|
11
|
+
const res = await fetch(url, {
|
|
12
|
+
headers: { Accept: "application/json", "User-Agent": "claude-local-docs/1.0" },
|
|
13
|
+
signal: AbortSignal.timeout(15_000),
|
|
14
|
+
});
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
throw new Error(`npm registry returned ${res.status} for ${library}`);
|
|
17
|
+
}
|
|
18
|
+
const pkg = (await res.json());
|
|
19
|
+
let repoUrl;
|
|
20
|
+
let repoOrg;
|
|
21
|
+
let repoName;
|
|
22
|
+
const rawRepo = typeof pkg.repository === "string" ? pkg.repository : pkg.repository?.url;
|
|
23
|
+
if (rawRepo) {
|
|
24
|
+
repoUrl = normalizeRepoUrl(rawRepo);
|
|
25
|
+
const m = repoUrl.match(/github\.com\/([^/]+)\/([^/]+)/);
|
|
26
|
+
if (m) {
|
|
27
|
+
repoOrg = m[1];
|
|
28
|
+
repoName = m[2];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Extract llms/llmsFull fields (Colin Hacks convention for AI autodiscovery)
|
|
32
|
+
const llmsUrl = typeof pkg.llms === "string" ? pkg.llms : undefined;
|
|
33
|
+
const llmsFullUrl = typeof pkg.llmsFull === "string" ? pkg.llmsFull : undefined;
|
|
34
|
+
return {
|
|
35
|
+
name: pkg.name,
|
|
36
|
+
version: pkg.version,
|
|
37
|
+
homepage: pkg.homepage || undefined,
|
|
38
|
+
repoUrl,
|
|
39
|
+
repoOrg,
|
|
40
|
+
repoName,
|
|
41
|
+
llmsUrl,
|
|
42
|
+
llmsFullUrl,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function normalizeRepoUrl(raw) {
|
|
46
|
+
return raw
|
|
47
|
+
.replace(/^git\+/, "")
|
|
48
|
+
.replace(/^git:\/\//, "https://")
|
|
49
|
+
.replace(/^ssh:\/\/git@github\.com/, "https://github.com")
|
|
50
|
+
.replace(/\.git$/, "");
|
|
51
|
+
}
|
|
52
|
+
// ── Candidate URL generation ───────────────────────────────────────────
|
|
53
|
+
export function generateCandidateUrls(info) {
|
|
54
|
+
const urls = [];
|
|
55
|
+
const seen = new Set();
|
|
56
|
+
const add = (url) => {
|
|
57
|
+
if (!seen.has(url)) {
|
|
58
|
+
seen.add(url);
|
|
59
|
+
urls.push(url);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
// ── Priority 1: package.json llms/llmsFull fields (most reliable) ──
|
|
63
|
+
if (info.llmsFullUrl)
|
|
64
|
+
add(info.llmsFullUrl);
|
|
65
|
+
if (info.llmsUrl)
|
|
66
|
+
add(info.llmsUrl);
|
|
67
|
+
// ── Priority 2: homepage-based probing ──
|
|
68
|
+
if (info.homepage) {
|
|
69
|
+
const hp = info.homepage.replace(/\/$/, "");
|
|
70
|
+
add(`${hp}/llms-full.txt`);
|
|
71
|
+
add(`${hp}/llms.txt`);
|
|
72
|
+
// docs.{domain} variant (Mintlify/GitBook auto-generate pattern)
|
|
73
|
+
try {
|
|
74
|
+
const u = new URL(hp);
|
|
75
|
+
if (!u.hostname.startsWith("docs.")) {
|
|
76
|
+
const docsHost = `docs.${u.hostname}`;
|
|
77
|
+
add(`${u.protocol}//${docsHost}/llms-full.txt`);
|
|
78
|
+
add(`${u.protocol}//${docsHost}/llms.txt`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// invalid URL — skip
|
|
83
|
+
}
|
|
84
|
+
// /docs/ subpath variant
|
|
85
|
+
try {
|
|
86
|
+
const u = new URL(hp);
|
|
87
|
+
add(`${u.origin}/docs/llms-full.txt`);
|
|
88
|
+
add(`${u.origin}/docs/llms.txt`);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// invalid URL — skip
|
|
92
|
+
}
|
|
93
|
+
// llms.{domain} subdomain (Motion-style pattern)
|
|
94
|
+
try {
|
|
95
|
+
const u = new URL(hp);
|
|
96
|
+
if (!u.hostname.startsWith("llms.")) {
|
|
97
|
+
add(`${u.protocol}//llms.${u.hostname}/llms-full.txt`);
|
|
98
|
+
add(`${u.protocol}//llms.${u.hostname}/llms.txt`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// invalid URL — skip
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// ── Priority 3: GitHub raw ──
|
|
106
|
+
if (info.repoOrg && info.repoName) {
|
|
107
|
+
for (const branch of ["main", "master"]) {
|
|
108
|
+
add(`https://raw.githubusercontent.com/${info.repoOrg}/${info.repoName}/${branch}/llms-full.txt`);
|
|
109
|
+
add(`https://raw.githubusercontent.com/${info.repoOrg}/${info.repoName}/${branch}/llms.txt`);
|
|
110
|
+
}
|
|
111
|
+
// README.md fallback (before homepage HTML)
|
|
112
|
+
for (const branch of ["main", "master"]) {
|
|
113
|
+
add(`https://raw.githubusercontent.com/${info.repoOrg}/${info.repoName}/${branch}/README.md`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// ── Priority 4: Homepage HTML fallback (last) ──
|
|
117
|
+
if (info.homepage) {
|
|
118
|
+
add(info.homepage);
|
|
119
|
+
}
|
|
120
|
+
return urls;
|
|
121
|
+
}
|
|
122
|
+
// ── Index detection ────────────────────────────────────────────────────
|
|
123
|
+
const MD_LINK_RE = /\[([^\]]*)\]\(([^)]+)\)/g;
|
|
124
|
+
export function detectIndex(content, url) {
|
|
125
|
+
// Large files are content, not an index
|
|
126
|
+
if (content.length > 100_000) {
|
|
127
|
+
return { isIndex: false, links: [] };
|
|
128
|
+
}
|
|
129
|
+
const links = [];
|
|
130
|
+
for (const m of content.matchAll(MD_LINK_RE)) {
|
|
131
|
+
const href = m[2].trim();
|
|
132
|
+
// Only keep http(s) links and relative paths that look like docs
|
|
133
|
+
if (href.startsWith("http") || href.startsWith("/") || href.endsWith(".md") || href.endsWith(".txt")) {
|
|
134
|
+
links.push({ text: m[1], url: href });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (links.length < 5) {
|
|
138
|
+
return { isIndex: false, links: [] };
|
|
139
|
+
}
|
|
140
|
+
// Count lines that are mostly links vs prose
|
|
141
|
+
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
142
|
+
const linkTestRe = /\[[^\]]*\]\([^)]+\)/;
|
|
143
|
+
let linkLines = 0;
|
|
144
|
+
for (const line of lines) {
|
|
145
|
+
if (linkTestRe.test(line))
|
|
146
|
+
linkLines++;
|
|
147
|
+
}
|
|
148
|
+
const linkRatio = linkLines / (lines.length || 1);
|
|
149
|
+
const isIndex = linkRatio > 0.5 && links.length > 5;
|
|
150
|
+
return { isIndex, links };
|
|
151
|
+
}
|
|
152
|
+
// ── Index expansion ────────────────────────────────────────────────────
|
|
153
|
+
const MAX_EXPAND_LINKS = 100;
|
|
154
|
+
const CONCURRENCY = 5;
|
|
155
|
+
const INTER_REQUEST_DELAY_MS = 200;
|
|
156
|
+
export async function expandIndex(links, baseUrl) {
|
|
157
|
+
const expandedUrls = [];
|
|
158
|
+
const failedUrls = [];
|
|
159
|
+
const parts = [];
|
|
160
|
+
// Resolve and dedupe URLs
|
|
161
|
+
const resolved = [];
|
|
162
|
+
const seen = new Set();
|
|
163
|
+
for (const link of links.slice(0, MAX_EXPAND_LINKS)) {
|
|
164
|
+
const absolute = resolveUrl(link.url, baseUrl);
|
|
165
|
+
if (!absolute || seen.has(absolute))
|
|
166
|
+
continue;
|
|
167
|
+
// Skip binary / non-doc URLs
|
|
168
|
+
if (/\.(png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|zip|tar|gz|mp4|mp3|pdf)$/i.test(absolute)) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
seen.add(absolute);
|
|
172
|
+
resolved.push({ text: link.text, absolute });
|
|
173
|
+
}
|
|
174
|
+
// Fetch with concurrency limit
|
|
175
|
+
let i = 0;
|
|
176
|
+
while (i < resolved.length) {
|
|
177
|
+
const batch = resolved.slice(i, i + CONCURRENCY);
|
|
178
|
+
const results = await Promise.allSettled(batch.map(async (item) => {
|
|
179
|
+
const result = await fetchDocContent(item.absolute, { timeoutMs: 30_000 });
|
|
180
|
+
return { item, result };
|
|
181
|
+
}));
|
|
182
|
+
for (let j = 0; j < results.length; j++) {
|
|
183
|
+
const r = results[j];
|
|
184
|
+
if (r.status === "fulfilled" && r.value.result.ok) {
|
|
185
|
+
const { item, result } = r.value;
|
|
186
|
+
const ct = result.contentType.toLowerCase();
|
|
187
|
+
const md = ct.includes("html") ? htmlToMarkdown(result.content) : result.content;
|
|
188
|
+
parts.push(`## ${item.text}\n\n${md}`);
|
|
189
|
+
expandedUrls.push(item.absolute);
|
|
190
|
+
}
|
|
191
|
+
else if (r.status === "fulfilled") {
|
|
192
|
+
failedUrls.push(r.value.item.absolute);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
failedUrls.push(batch[j].absolute);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
i += CONCURRENCY;
|
|
199
|
+
if (i < resolved.length) {
|
|
200
|
+
await sleep(INTER_REQUEST_DELAY_MS);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
content: parts.join("\n\n---\n\n"),
|
|
205
|
+
expandedUrls,
|
|
206
|
+
failedUrls,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function resolveUrl(href, base) {
|
|
210
|
+
try {
|
|
211
|
+
return new URL(href, base).toString();
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function sleep(ms) {
|
|
218
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
219
|
+
}
|
|
220
|
+
// ── HTML → Markdown ────────────────────────────────────────────────────
|
|
221
|
+
// Simple tag stripper for specific elements (works on raw HTML before turndown)
|
|
222
|
+
function stripTags(html, tags) {
|
|
223
|
+
let result = html;
|
|
224
|
+
for (const tag of tags) {
|
|
225
|
+
// Remove opening and closing tags and everything between them
|
|
226
|
+
const re = new RegExp(`<${tag}[^>]*>[\\s\\S]*?</${tag}>`, "gi");
|
|
227
|
+
result = result.replace(re, "");
|
|
228
|
+
// Also remove self-closing variants
|
|
229
|
+
const selfRe = new RegExp(`<${tag}[^/>]*/?>`, "gi");
|
|
230
|
+
result = result.replace(selfRe, "");
|
|
231
|
+
}
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
function extractMainContent(html) {
|
|
235
|
+
// Try <main>, then <article>, then <body>, else full HTML
|
|
236
|
+
for (const tag of ["main", "article"]) {
|
|
237
|
+
const re = new RegExp(`<${tag}[^>]*>([\\s\\S]*?)</${tag}>`, "i");
|
|
238
|
+
const m = html.match(re);
|
|
239
|
+
if (m)
|
|
240
|
+
return m[1];
|
|
241
|
+
}
|
|
242
|
+
const bodyMatch = html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
|
|
243
|
+
if (bodyMatch)
|
|
244
|
+
return bodyMatch[1];
|
|
245
|
+
return html;
|
|
246
|
+
}
|
|
247
|
+
let turndownInstance = null;
|
|
248
|
+
function getTurndown() {
|
|
249
|
+
if (turndownInstance)
|
|
250
|
+
return turndownInstance;
|
|
251
|
+
turndownInstance = new TurndownService({
|
|
252
|
+
headingStyle: "atx",
|
|
253
|
+
codeBlockStyle: "fenced",
|
|
254
|
+
bulletListMarker: "-",
|
|
255
|
+
});
|
|
256
|
+
turndownInstance.use(gfm);
|
|
257
|
+
turndownInstance.addRule("removeImages", {
|
|
258
|
+
filter: "img",
|
|
259
|
+
replacement: () => "",
|
|
260
|
+
});
|
|
261
|
+
return turndownInstance;
|
|
262
|
+
}
|
|
263
|
+
export function htmlToMarkdown(html) {
|
|
264
|
+
let content = extractMainContent(html);
|
|
265
|
+
content = stripTags(content, ["nav", "footer", "header", "script", "style", "aside", "noscript"]);
|
|
266
|
+
return getTurndown().turndown(content);
|
|
267
|
+
}
|
|
268
|
+
function classifySource(url, npmInfo) {
|
|
269
|
+
// Check if URL was from package.json llms/llmsFull fields
|
|
270
|
+
if (npmInfo && (url === npmInfo.llmsFullUrl || url === npmInfo.llmsUrl))
|
|
271
|
+
return "npm-llms-field";
|
|
272
|
+
if (url.endsWith("/llms-full.txt"))
|
|
273
|
+
return "llms-full.txt";
|
|
274
|
+
if (url.endsWith("/llms.txt"))
|
|
275
|
+
return "llms.txt";
|
|
276
|
+
if (url.includes("raw.githubusercontent.com") && url.endsWith("/README.md"))
|
|
277
|
+
return "readme";
|
|
278
|
+
if (url.includes("raw.githubusercontent.com"))
|
|
279
|
+
return "github-raw";
|
|
280
|
+
return "homepage-html";
|
|
281
|
+
}
|
|
282
|
+
// ── Orchestrator ───────────────────────────────────────────────────────
|
|
283
|
+
export async function resolveDocsUrl(library) {
|
|
284
|
+
// 1. Query npm registry (also extracts llms/llmsFull package.json fields)
|
|
285
|
+
const npmInfo = await queryNpmRegistry(library);
|
|
286
|
+
// 2. Generate candidate URLs (npm fields first, then homepage, docs subdomain, etc.)
|
|
287
|
+
const candidates = generateCandidateUrls(npmInfo);
|
|
288
|
+
if (candidates.length === 0) {
|
|
289
|
+
throw new Error(`No candidate documentation URLs found for ${library}`);
|
|
290
|
+
}
|
|
291
|
+
// 3. Probe each candidate
|
|
292
|
+
for (const candidateUrl of candidates) {
|
|
293
|
+
const result = await fetchDocContent(candidateUrl, { timeoutMs: 15_000 });
|
|
294
|
+
if (!result.ok)
|
|
295
|
+
continue;
|
|
296
|
+
const ct = result.contentType.toLowerCase();
|
|
297
|
+
const isBinary = ct.includes("image") ||
|
|
298
|
+
ct.includes("font") ||
|
|
299
|
+
ct.includes("octet-stream") ||
|
|
300
|
+
ct.includes("pdf") ||
|
|
301
|
+
ct.includes("zip");
|
|
302
|
+
if (isBinary)
|
|
303
|
+
continue;
|
|
304
|
+
// Determine source label
|
|
305
|
+
const source = classifySource(candidateUrl, npmInfo);
|
|
306
|
+
const isHtml = ct.includes("html");
|
|
307
|
+
// 4. HTML → convert to markdown
|
|
308
|
+
if (isHtml) {
|
|
309
|
+
const md = htmlToMarkdown(result.content);
|
|
310
|
+
if (md.trim().length > 100) {
|
|
311
|
+
const discoveryResult = {
|
|
312
|
+
url: candidateUrl,
|
|
313
|
+
content: md,
|
|
314
|
+
byteLength: Buffer.byteLength(md),
|
|
315
|
+
source: "homepage-html",
|
|
316
|
+
};
|
|
317
|
+
if (md.length < 5_000) {
|
|
318
|
+
discoveryResult.warning = "Content is very small, index may be thin";
|
|
319
|
+
}
|
|
320
|
+
return discoveryResult;
|
|
321
|
+
}
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
// 5. Text docs — check if it's an index that needs expansion
|
|
325
|
+
const detection = detectIndex(result.content, candidateUrl);
|
|
326
|
+
if (detection.isIndex) {
|
|
327
|
+
const expanded = await expandIndex(detection.links, candidateUrl);
|
|
328
|
+
if (expanded.content.length > 0) {
|
|
329
|
+
const discoveryResult = {
|
|
330
|
+
url: candidateUrl,
|
|
331
|
+
content: expanded.content,
|
|
332
|
+
byteLength: Buffer.byteLength(expanded.content),
|
|
333
|
+
source: "llms.txt-index",
|
|
334
|
+
expandedUrls: expanded.expandedUrls,
|
|
335
|
+
failedUrls: expanded.failedUrls.length > 0 ? expanded.failedUrls : undefined,
|
|
336
|
+
};
|
|
337
|
+
if (expanded.content.length < 5_000) {
|
|
338
|
+
discoveryResult.warning = "Content is very small, index may be thin";
|
|
339
|
+
}
|
|
340
|
+
return discoveryResult;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// Full content doc (npm-llms-field, llms-full.txt, non-index llms.txt, github-raw, or readme)
|
|
344
|
+
const discoveryResult = {
|
|
345
|
+
url: candidateUrl,
|
|
346
|
+
content: result.content,
|
|
347
|
+
byteLength: result.byteLength,
|
|
348
|
+
source,
|
|
349
|
+
};
|
|
350
|
+
if (result.content.length < 5_000) {
|
|
351
|
+
discoveryResult.warning = "Content is very small, index may be thin";
|
|
352
|
+
}
|
|
353
|
+
return discoveryResult;
|
|
354
|
+
}
|
|
355
|
+
throw new Error(`No documentation found for ${library}. Tried ${candidates.length} candidate URLs.`);
|
|
356
|
+
}
|
|
357
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}
|
package/dist/fetcher.d.ts
CHANGED
|
@@ -7,8 +7,13 @@ export type FetchResult = {
|
|
|
7
7
|
content: string;
|
|
8
8
|
byteLength: number;
|
|
9
9
|
contentType: string;
|
|
10
|
+
finalUrl: string;
|
|
10
11
|
} | {
|
|
11
12
|
ok: false;
|
|
12
13
|
error: string;
|
|
13
14
|
};
|
|
14
|
-
export
|
|
15
|
+
export interface FetchOptions {
|
|
16
|
+
/** Timeout in milliseconds (default 120s). Use 15s for URL probing. */
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function fetchDocContent(url: string, options?: FetchOptions): Promise<FetchResult>;
|
package/dist/fetcher.js
CHANGED
|
@@ -2,17 +2,19 @@
|
|
|
2
2
|
* Raw HTTP fetch for documentation content.
|
|
3
3
|
* No AI processing, no truncation — returns full text as-is.
|
|
4
4
|
*/
|
|
5
|
-
const
|
|
6
|
-
const MAX_SIZE =
|
|
7
|
-
export async function fetchDocContent(url) {
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
6
|
+
const MAX_SIZE = 200 * 1024 * 1024; // 200MB
|
|
7
|
+
export async function fetchDocContent(url, options) {
|
|
8
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
8
9
|
try {
|
|
9
10
|
const controller = new AbortController();
|
|
10
|
-
const timer = setTimeout(() => controller.abort(),
|
|
11
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
11
12
|
const res = await fetch(url, {
|
|
12
13
|
signal: controller.signal,
|
|
13
14
|
headers: {
|
|
14
15
|
"User-Agent": "claude-local-docs/1.0",
|
|
15
16
|
"Accept": "text/plain, text/markdown, text/html, */*",
|
|
17
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
16
18
|
},
|
|
17
19
|
redirect: "follow",
|
|
18
20
|
});
|
|
@@ -37,11 +39,12 @@ export async function fetchDocContent(url) {
|
|
|
37
39
|
content,
|
|
38
40
|
byteLength: buffer.byteLength,
|
|
39
41
|
contentType,
|
|
42
|
+
finalUrl: res.url,
|
|
40
43
|
};
|
|
41
44
|
}
|
|
42
45
|
catch (err) {
|
|
43
46
|
if (err.name === "AbortError") {
|
|
44
|
-
return { ok: false, error: `Request timed out after ${
|
|
47
|
+
return { ok: false, error: `Request timed out after ${timeoutMs / 1000}s` };
|
|
45
48
|
}
|
|
46
49
|
return { ok: false, error: err.message ?? String(err) };
|
|
47
50
|
}
|
package/dist/fetcher.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetcher.js","sourceRoot":"","sources":["../src/fetcher.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,
|
|
1
|
+
{"version":3,"file":"fetcher.js","sourceRoot":"","sources":["../src/fetcher.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,kBAAkB,GAAG,OAAO,CAAC;AACnC,MAAM,QAAQ,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,QAAQ;AAW5C,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAW,EACX,OAAsB;IAEtB,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,kBAAkB,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;QAE9D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,OAAO,EAAE;gBACP,YAAY,EAAE,uBAAuB;gBACrC,QAAQ,EAAE,2CAA2C;gBACrD,iBAAiB,EAAE,gBAAgB;aACpC;YACD,QAAQ,EAAE,QAAQ;SACnB,CAAC,CAAC;QAEH,YAAY,CAAC,KAAK,CAAC,CAAC;QAEpB,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC;QACtE,CAAC;QAED,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,YAAY,CAAC;QAEpE,2CAA2C;QAC3C,MAAM,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACxD,IAAI,aAAa,IAAI,QAAQ,CAAC,aAAa,EAAE,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC;YAC5D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,sBAAsB,aAAa,kBAAkB,QAAQ,GAAG,EAAE,CAAC;QAChG,CAAC;QAED,4BAA4B;QAC5B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;QACvC,IAAI,MAAM,CAAC,UAAU,GAAG,QAAQ,EAAE,CAAC;YACjC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,sBAAsB,MAAM,CAAC,UAAU,kBAAkB,QAAQ,GAAG,EAAE,CAAC;QACpG,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEjD,OAAO;YACL,EAAE,EAAE,IAAI;YACR,OAAO;YACP,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,WAAW;YACX,QAAQ,EAAE,GAAG,CAAC,GAAG;SAClB,CAAC;IACJ,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAC9B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,2BAA2B,SAAS,GAAG,IAAI,GAAG,EAAE,CAAC;QAC9E,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC1D,CAAC;AACH,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,42 @@ import { indexDocument } from "./indexer.js";
|
|
|
8
8
|
import { searchDocs } from "./search.js";
|
|
9
9
|
import { analyzeDependencies } from "./workspace.js";
|
|
10
10
|
import { fetchDocContent } from "./fetcher.js";
|
|
11
|
+
import { resolveDocsUrl } from "./discovery.js";
|
|
11
12
|
const projectRoot = resolveProjectRoot();
|
|
13
|
+
/**
|
|
14
|
+
* Expand search results with adjacent chunks (id-1 and id+1) from the same library/section.
|
|
15
|
+
* This recovers context when code examples are split across chunk boundaries.
|
|
16
|
+
*/
|
|
17
|
+
async function expandWithNeighbors(results, store) {
|
|
18
|
+
const resultIds = new Set(results.map((r) => r.chunkId));
|
|
19
|
+
const expanded = [];
|
|
20
|
+
for (const result of results) {
|
|
21
|
+
const headingJson = JSON.stringify(result.headingPath);
|
|
22
|
+
const parts = [];
|
|
23
|
+
// Try previous chunk
|
|
24
|
+
const prevId = result.chunkId - 1;
|
|
25
|
+
if (!resultIds.has(prevId)) {
|
|
26
|
+
const prev = await store.getChunkById(prevId);
|
|
27
|
+
if (prev && prev.library === result.library && prev.headingPath === headingJson) {
|
|
28
|
+
parts.push(prev.text);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
parts.push(result.content);
|
|
32
|
+
// Try next chunk
|
|
33
|
+
const nextId = result.chunkId + 1;
|
|
34
|
+
if (!resultIds.has(nextId)) {
|
|
35
|
+
const next = await store.getChunkById(nextId);
|
|
36
|
+
if (next && next.library === result.library && next.headingPath === headingJson) {
|
|
37
|
+
parts.push(next.text);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
expanded.push({
|
|
41
|
+
...result,
|
|
42
|
+
content: parts.length > 1 ? parts.join("\n\n") : result.content,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return expanded;
|
|
46
|
+
}
|
|
12
47
|
const store = new DocStore(projectRoot);
|
|
13
48
|
const server = new McpServer({
|
|
14
49
|
name: "local-docs",
|
|
@@ -104,14 +139,14 @@ server.registerTool("search_docs", {
|
|
|
104
139
|
};
|
|
105
140
|
}
|
|
106
141
|
const results = await searchDocs(query, store, { library, topK });
|
|
107
|
-
|
|
142
|
+
// Expand results with adjacent chunks for fuller context
|
|
143
|
+
const expanded = await expandWithNeighbors(results, store);
|
|
144
|
+
const formatted = expanded.map((r) => ({
|
|
108
145
|
score: r.score,
|
|
109
146
|
library: r.library,
|
|
110
147
|
heading: r.headingPath.join(" > "),
|
|
111
148
|
chunkId: r.chunkId,
|
|
112
|
-
content: r.content
|
|
113
|
-
? r.content.slice(0, 500) + "..."
|
|
114
|
-
: r.content,
|
|
149
|
+
content: r.content,
|
|
115
150
|
}));
|
|
116
151
|
return {
|
|
117
152
|
content: [{ type: "text", text: JSON.stringify(formatted, null, 2) }],
|
|
@@ -237,7 +272,7 @@ server.registerTool("get_doc_section", {
|
|
|
237
272
|
});
|
|
238
273
|
// --- Tool 6: fetch_and_store_doc ---
|
|
239
274
|
server.registerTool("fetch_and_store_doc", {
|
|
240
|
-
description: "Fetch documentation from a URL (raw HTTP, no AI processing or truncation) and index it. Use this for llms.txt and llms-full.txt URLs to preserve full content. Handles up to
|
|
275
|
+
description: "Fetch documentation from a URL (raw HTTP, no AI processing or truncation) and index it. Use this for llms.txt and llms-full.txt URLs to preserve full content. Handles up to 200MB, 120s timeout.",
|
|
241
276
|
inputSchema: {
|
|
242
277
|
library: z.string().describe("Library name (e.g. 'react', '@tanstack/query')"),
|
|
243
278
|
version: z.string().describe("Library version"),
|
|
@@ -292,6 +327,51 @@ server.registerTool("fetch_and_store_doc", {
|
|
|
292
327
|
};
|
|
293
328
|
}
|
|
294
329
|
});
|
|
330
|
+
// --- Tool 7: discover_and_fetch_docs ---
|
|
331
|
+
server.registerTool("discover_and_fetch_docs", {
|
|
332
|
+
description: "Discover, fetch, and index documentation for a library automatically. Checks package.json llms/llmsFull fields first, then probes homepage, docs.{domain}, llms.{domain}, /docs/ subpath, and GitHub raw for llms-full.txt/llms.txt. Detects index files and expands them. Falls back to homepage HTML → markdown conversion. Self-contained — no WebSearch needed.",
|
|
333
|
+
inputSchema: {
|
|
334
|
+
library: z.string().describe("npm package name (e.g. 'react', '@tanstack/query')"),
|
|
335
|
+
version: z.string().optional().describe("Library version (optional, auto-detected from npm)"),
|
|
336
|
+
},
|
|
337
|
+
}, async ({ library, version }) => {
|
|
338
|
+
try {
|
|
339
|
+
const discovery = await resolveDocsUrl(library);
|
|
340
|
+
await store.saveRawDoc(library, discovery.content);
|
|
341
|
+
const chunks = await indexDocument(discovery.content, library);
|
|
342
|
+
const result = await store.addLibrary(library, version ?? "latest", discovery.url, chunks);
|
|
343
|
+
return {
|
|
344
|
+
content: [
|
|
345
|
+
{
|
|
346
|
+
type: "text",
|
|
347
|
+
text: JSON.stringify({
|
|
348
|
+
success: true,
|
|
349
|
+
library,
|
|
350
|
+
source: discovery.source,
|
|
351
|
+
url: discovery.url,
|
|
352
|
+
chunkCount: result.chunkCount,
|
|
353
|
+
byteLength: discovery.byteLength,
|
|
354
|
+
totalIndexSize: result.indexSize,
|
|
355
|
+
expandedUrls: discovery.expandedUrls,
|
|
356
|
+
failedUrls: discovery.failedUrls,
|
|
357
|
+
warning: discovery.warning,
|
|
358
|
+
}),
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
catch (err) {
|
|
364
|
+
return {
|
|
365
|
+
content: [
|
|
366
|
+
{
|
|
367
|
+
type: "text",
|
|
368
|
+
text: JSON.stringify({ success: false, library, error: err.message }),
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
isError: true,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
});
|
|
295
375
|
// --- Start server ---
|
|
296
376
|
async function main() {
|
|
297
377
|
const transport = new StdioServerTransport();
|