@farming-labs/docs 0.1.50 → 0.1.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-CuYI2Nji.mjs → agent-C3rj3o1l.mjs} +148 -13
- package/dist/{agent-CbAtuZAc.mjs → agent-Xh0UaY5I.mjs} +311 -2
- package/dist/cli/index.mjs +10 -7
- package/dist/{doctor-D0UYKEFN.mjs → doctor-B0xEGzBU.mjs} +119 -7
- package/dist/index.d.mts +25 -3
- package/dist/index.mjs +3 -4
- package/dist/{init-CShiL8Ch.mjs → init-C1tsFxXo.mjs} +1 -1
- package/dist/{mcp-CYpMeMfi.mjs → mcp-C-TmMrdw.mjs} +2 -2
- package/dist/mcp.d.mts +1 -1
- package/dist/mcp.mjs +2 -2
- package/dist/{search-D7DS0MLc.mjs → search-C7mKYgqQ.mjs} +3 -3
- package/dist/{search-8oEskRtz.mjs → search-Cu_pxL8o.mjs} +77 -1
- package/dist/{search-DcI00OQT.d.mts → search-KMMtXPTi.d.mts} +1 -1
- package/dist/server.d.mts +2 -2
- package/dist/server.mjs +2 -2
- package/dist/{upgrade-2xcgMsj6.mjs → upgrade-a185_G5A.mjs} +1 -1
- package/package.json +1 -1
- package/dist/reading-time-DCZXC6-3.mjs +0 -311
- /package/dist/{api-reference-y7cqtq4w.mjs → api-reference-GDAEzQn1.mjs} +0 -0
- /package/dist/{config-C7sUsMkm.mjs → config-Si-yUfM_.mjs} +0 -0
- /package/dist/{types-Agkn2EQE.d.mts → types-Cl6WPIHa.d.mts} +0 -0
- /package/dist/{utils-DSMXVnEu.mjs → utils-CpTFbAiS.mjs} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import {
|
|
1
|
+
import { C as renderDocsMarkdownDocument, g as findDocsMarkdownPage } from "./agent-Xh0UaY5I.mjs";
|
|
2
|
+
import { d as hashGeneratedAgentContent, m as serializeGeneratedAgentDocument, p as parseGeneratedAgentDocument, u as GENERATED_AGENT_PROVENANCE_VERSION } from "./search-Cu_pxL8o.mjs";
|
|
3
3
|
import "./index.mjs";
|
|
4
|
-
import "./api-reference-
|
|
4
|
+
import "./api-reference-GDAEzQn1.mjs";
|
|
5
5
|
import { createFilesystemDocsMcpSource } from "./mcp.mjs";
|
|
6
6
|
import "./server.mjs";
|
|
7
|
-
import { a as loadProjectEnv, c as readNavTitle, d as readTopLevelStringProperty, f as resolveDocsConfigPath, i as loadDocsConfigModule, l as readNumberProperty, o as readBooleanProperty, p as resolveDocsContentDir, s as readEnvReferenceProperty, t as extractNestedObjectLiteral, u as readStringProperty } from "./config-
|
|
7
|
+
import { a as loadProjectEnv, c as readNavTitle, d as readTopLevelStringProperty, f as resolveDocsConfigPath, i as loadDocsConfigModule, l as readNumberProperty, o as readBooleanProperty, p as resolveDocsContentDir, s as readEnvReferenceProperty, t as extractNestedObjectLiteral, u as readStringProperty } from "./config-Si-yUfM_.mjs";
|
|
8
8
|
import matter from "gray-matter";
|
|
9
9
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
10
10
|
import path from "node:path";
|
|
@@ -50,6 +50,14 @@ function parseAgentCompactArgs(argv) {
|
|
|
50
50
|
parsed.dryRun = true;
|
|
51
51
|
continue;
|
|
52
52
|
}
|
|
53
|
+
if (arg === "--stale") {
|
|
54
|
+
parsed.stale = true;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (arg === "--include-missing") {
|
|
58
|
+
parsed.includeMissing = true;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
53
61
|
if (arg === "--protect-json") {
|
|
54
62
|
const nextValue = argv[index + 1];
|
|
55
63
|
if (nextValue && !nextValue.startsWith("--")) {
|
|
@@ -221,6 +229,95 @@ function readPageTokenBudget(pagePath) {
|
|
|
221
229
|
const { data } = matter(readFileSync(pagePath, "utf-8"));
|
|
222
230
|
return normalizeTokenBudget(data.agent?.tokenBudget);
|
|
223
231
|
}
|
|
232
|
+
function buildCompactionSettingsHash(options) {
|
|
233
|
+
return hashGeneratedAgentContent(JSON.stringify({
|
|
234
|
+
model: options.model ?? DEFAULT_TTC_MODEL,
|
|
235
|
+
aggressiveness: options.aggressiveness ?? DEFAULT_TTC_AGGRESSIVENESS,
|
|
236
|
+
maxOutputTokens: options.maxOutputTokens ?? null,
|
|
237
|
+
minOutputTokens: options.minOutputTokens ?? null,
|
|
238
|
+
protectJson: options.protectJson ?? null
|
|
239
|
+
}));
|
|
240
|
+
}
|
|
241
|
+
function buildPageOptions(defaults, pagePath) {
|
|
242
|
+
const tokenBudget = readPageTokenBudget(pagePath);
|
|
243
|
+
const pageOptions = mergeAgentCompactOptions(defaults, { maxOutputTokens: tokenBudget });
|
|
244
|
+
if (pageOptions.minOutputTokens !== void 0 && pageOptions.maxOutputTokens !== void 0 && pageOptions.minOutputTokens > pageOptions.maxOutputTokens) pageOptions.minOutputTokens = pageOptions.maxOutputTokens;
|
|
245
|
+
return {
|
|
246
|
+
pageOptions,
|
|
247
|
+
tokenBudget
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function buildResolvedPageSourceDocument(page) {
|
|
251
|
+
return renderDocsMarkdownDocument({
|
|
252
|
+
...page,
|
|
253
|
+
agentRawContent: void 0
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
function buildAgentSourceDocument(page) {
|
|
257
|
+
if (typeof page.agentRawContent === "string") return page.agentRawContent;
|
|
258
|
+
return renderDocsMarkdownDocument(page);
|
|
259
|
+
}
|
|
260
|
+
function readCurrentAgentDocument(target) {
|
|
261
|
+
if (!target.hasAgentFile || !existsSync(target.agentPath)) return void 0;
|
|
262
|
+
return parseGeneratedAgentDocument(readFileSync(target.agentPath, "utf-8"));
|
|
263
|
+
}
|
|
264
|
+
function resolveSourceKindForCompaction(target, currentDocument) {
|
|
265
|
+
if (!target.hasAgentFile) return "resolved-page";
|
|
266
|
+
if (currentDocument?.provenance?.sourceKind === "resolved-page") return "resolved-page";
|
|
267
|
+
return "agent-md";
|
|
268
|
+
}
|
|
269
|
+
function buildSourceDocumentForCompaction(page, sourceKind) {
|
|
270
|
+
return sourceKind === "resolved-page" ? buildResolvedPageSourceDocument(page) : buildAgentSourceDocument(page);
|
|
271
|
+
}
|
|
272
|
+
function buildGeneratedAgentProvenance(sourceKind, sourceDocument, output, pageOptions) {
|
|
273
|
+
return {
|
|
274
|
+
version: GENERATED_AGENT_PROVENANCE_VERSION,
|
|
275
|
+
sourceKind,
|
|
276
|
+
sourceHash: hashGeneratedAgentContent(sourceDocument),
|
|
277
|
+
settingsHash: buildCompactionSettingsHash(pageOptions),
|
|
278
|
+
outputHash: hashGeneratedAgentContent(output),
|
|
279
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function inspectAgentCompactionState(page, target, defaults) {
|
|
283
|
+
const { pageOptions, tokenBudget } = buildPageOptions(defaults, target.pagePath);
|
|
284
|
+
const currentDocument = readCurrentAgentDocument(target);
|
|
285
|
+
if (!currentDocument) return {
|
|
286
|
+
status: "missing",
|
|
287
|
+
sourceKind: "resolved-page",
|
|
288
|
+
pageOptions,
|
|
289
|
+
sourceDocument: buildResolvedPageSourceDocument(page),
|
|
290
|
+
tokenBudget
|
|
291
|
+
};
|
|
292
|
+
const sourceKind = resolveSourceKindForCompaction(target, currentDocument);
|
|
293
|
+
const sourceDocument = buildSourceDocumentForCompaction(page, sourceKind);
|
|
294
|
+
if (!currentDocument.provenance) return {
|
|
295
|
+
status: "unknown",
|
|
296
|
+
sourceKind,
|
|
297
|
+
pageOptions,
|
|
298
|
+
sourceDocument,
|
|
299
|
+
tokenBudget
|
|
300
|
+
};
|
|
301
|
+
const outputModified = hashGeneratedAgentContent(currentDocument.content) !== currentDocument.provenance.outputHash;
|
|
302
|
+
if (currentDocument.provenance.sourceKind === "agent-md") return {
|
|
303
|
+
status: outputModified ? "modified" : "unknown",
|
|
304
|
+
sourceKind,
|
|
305
|
+
pageOptions,
|
|
306
|
+
sourceDocument,
|
|
307
|
+
provenance: currentDocument.provenance,
|
|
308
|
+
tokenBudget
|
|
309
|
+
};
|
|
310
|
+
const sourceChanged = hashGeneratedAgentContent(sourceDocument) !== currentDocument.provenance.sourceHash;
|
|
311
|
+
const settingsChanged = buildCompactionSettingsHash(pageOptions) !== currentDocument.provenance.settingsHash;
|
|
312
|
+
return {
|
|
313
|
+
status: outputModified && (sourceChanged || settingsChanged) ? "stale-modified" : outputModified ? "modified" : sourceChanged || settingsChanged ? "stale" : "fresh",
|
|
314
|
+
sourceKind,
|
|
315
|
+
pageOptions,
|
|
316
|
+
sourceDocument,
|
|
317
|
+
provenance: currentDocument.provenance,
|
|
318
|
+
tokenBudget
|
|
319
|
+
};
|
|
320
|
+
}
|
|
224
321
|
function protectForCompression(input) {
|
|
225
322
|
const segments = [];
|
|
226
323
|
const stash = (value) => {
|
|
@@ -311,8 +408,9 @@ async function compactAgentDocs(options = {}) {
|
|
|
311
408
|
const contentDir = typeof loadedConfigModule?.config.contentDir === "string" ? loadedConfigModule.config.contentDir : resolveDocsContentDir(rootDir, configContent, entry);
|
|
312
409
|
const siteTitle = typeof loadedConfigModule?.config.nav?.title === "string" ? loadedConfigModule.config.nav.title : readNavTitle(configContent) ?? "Documentation";
|
|
313
410
|
if (resolvedOptions.all && resolvedOptions.pages && resolvedOptions.pages.length > 0) throw new Error("Use either --all or specific page arguments, not both.");
|
|
411
|
+
if (resolvedOptions.includeMissing && !resolvedOptions.stale) throw new Error("Use --include-missing together with --stale.");
|
|
314
412
|
const requestedPages = resolvedOptions.pages?.filter((value) => value.trim().length > 0) ?? [];
|
|
315
|
-
if (!resolvedOptions.all && requestedPages.length === 0) throw new Error("Pass --all or at least one docs page slug/path to compact.");
|
|
413
|
+
if (!resolvedOptions.all && requestedPages.length === 0 && !resolvedOptions.stale) throw new Error("Pass --all, --stale, or at least one docs page slug/path to compact.");
|
|
316
414
|
const pages = await createFilesystemDocsMcpSource({
|
|
317
415
|
rootDir,
|
|
318
416
|
entry,
|
|
@@ -320,25 +418,58 @@ async function compactAgentDocs(options = {}) {
|
|
|
320
418
|
siteTitle
|
|
321
419
|
}).getPages();
|
|
322
420
|
if (pages.length === 0) throw new Error(`No docs content was found under ${contentDir}.`);
|
|
323
|
-
const selectedPages = resolveSelectedPages(pages, scanDocsPageTargets(rootDir, contentDir, entry), entry, requestedPages, resolvedOptions.all === true);
|
|
421
|
+
const selectedPages = resolveSelectedPages(pages, scanDocsPageTargets(rootDir, contentDir, entry), entry, requestedPages, resolvedOptions.all === true || resolvedOptions.stale === true && requestedPages.length === 0);
|
|
324
422
|
if (selectedPages.length === 0) throw new Error("No compactable docs pages matched the request.");
|
|
325
423
|
let created = 0;
|
|
326
424
|
let overwritten = 0;
|
|
425
|
+
let processed = 0;
|
|
426
|
+
let skippedFresh = 0;
|
|
427
|
+
let skippedModified = 0;
|
|
428
|
+
let skippedUnknown = 0;
|
|
429
|
+
let skippedMissing = 0;
|
|
430
|
+
const requestedExplicitPages = requestedPages.length > 0;
|
|
327
431
|
for (const { page, target } of selectedPages) {
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
432
|
+
const state = inspectAgentCompactionState(page, target, resolvedOptions);
|
|
433
|
+
if (resolvedOptions.stale) {
|
|
434
|
+
if (state.status === "fresh") {
|
|
435
|
+
skippedFresh += 1;
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
if (state.status === "modified" || state.status === "stale-modified") {
|
|
439
|
+
skippedModified += 1;
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (state.status === "unknown") {
|
|
443
|
+
skippedUnknown += 1;
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
if (state.status === "missing") {
|
|
447
|
+
if (!(resolvedOptions.includeMissing === true && (requestedExplicitPages || state.tokenBudget !== void 0))) {
|
|
448
|
+
skippedMissing += 1;
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
const compressed = await compressDocument(state.sourceDocument, state.pageOptions);
|
|
332
454
|
const nextContent = compressed.output.trimEnd();
|
|
455
|
+
const generatedDocument = serializeGeneratedAgentDocument(nextContent, buildGeneratedAgentProvenance(state.sourceKind, state.sourceDocument, nextContent, state.pageOptions));
|
|
333
456
|
console.log(pc.dim(`Compacting ${page.url} (${compressed.original_input_tokens ?? "?"} -> ${compressed.output_tokens ?? "?"} tokens)...`));
|
|
334
457
|
if (resolvedOptions.dryRun) continue;
|
|
335
458
|
mkdirSync(target.pageDir, { recursive: true });
|
|
336
|
-
writeFileSync(target.agentPath,
|
|
459
|
+
writeFileSync(target.agentPath, generatedDocument, "utf-8");
|
|
337
460
|
if (target.hasAgentFile) overwritten += 1;
|
|
338
461
|
else created += 1;
|
|
462
|
+
processed += 1;
|
|
463
|
+
}
|
|
464
|
+
if (resolvedOptions.dryRun) processed = selectedPages.length - skippedFresh - skippedModified - skippedUnknown - skippedMissing;
|
|
465
|
+
if (resolvedOptions.stale && processed === 0) {
|
|
466
|
+
console.log(pc.green("No stale generated agent.md files needed updates."));
|
|
467
|
+
if (skippedFresh + skippedModified + skippedUnknown + skippedMissing > 0) console.log(pc.dim(`Skipped ${skippedFresh} fresh, ${skippedModified} modified, ${skippedUnknown} unknown, and ${skippedMissing} missing page${skippedFresh + skippedModified + skippedUnknown + skippedMissing === 1 ? "" : "s"}.`));
|
|
468
|
+
return;
|
|
339
469
|
}
|
|
340
470
|
const summaryPrefix = resolvedOptions.dryRun ? "Dry run complete" : "Compaction complete";
|
|
341
|
-
console.log(pc.green(`${summaryPrefix}: ${
|
|
471
|
+
console.log(pc.green(`${summaryPrefix}: ${processed} page${processed === 1 ? "" : "s"} processed` + (resolvedOptions.dryRun ? "." : ` (${created} created, ${overwritten} overwritten).`)));
|
|
472
|
+
if (resolvedOptions.stale) console.log(pc.dim(`Skipped ${skippedFresh} fresh, ${skippedModified} modified, ${skippedUnknown} unknown, and ${skippedMissing} missing page${skippedFresh + skippedModified + skippedUnknown + skippedMissing === 1 ? "" : "s"}.`));
|
|
342
473
|
}
|
|
343
474
|
function printAgentCompactHelp() {
|
|
344
475
|
console.log(`
|
|
@@ -352,6 +483,8 @@ ${pc.dim("Examples:")}
|
|
|
352
483
|
${pc.cyan("npx @farming-labs/docs@latest agent compact /docs/installation")}
|
|
353
484
|
${pc.cyan("npx @farming-labs/docs@latest agent compact --page installation --page configuration")}
|
|
354
485
|
${pc.cyan("npx @farming-labs/docs@latest agent compact --all")}
|
|
486
|
+
${pc.cyan("npx @farming-labs/docs@latest agent compact --stale")}
|
|
487
|
+
${pc.cyan("npx @farming-labs/docs@latest agent compact --stale --include-missing")}
|
|
355
488
|
|
|
356
489
|
${pc.dim("Per-page override:")}
|
|
357
490
|
Add ${pc.cyan("agent.tokenBudget")} to a page frontmatter block to override the compact output target for that page.
|
|
@@ -359,6 +492,8 @@ ${pc.dim("Per-page override:")}
|
|
|
359
492
|
${pc.dim("Options:")}
|
|
360
493
|
${pc.cyan("--all")} Compact every folder-based docs page under the configured contentDir
|
|
361
494
|
${pc.cyan("--page <slug|path>")} Add a page explicitly (repeatable); positional page args work too
|
|
495
|
+
${pc.cyan("--stale")} Re-compact only stale generated agent.md files
|
|
496
|
+
${pc.cyan("--include-missing")} With ${pc.cyan("--stale")}, also create missing agent.md files for explicit pages or pages that define ${pc.cyan("agent.tokenBudget")}
|
|
362
497
|
${pc.cyan("--config <path>")} Use a custom docs config path instead of ${pc.dim("docs.config.ts[x]")}
|
|
363
498
|
${pc.cyan("--api-key <key>")} Token Company API key (or set ${pc.dim("TOKEN_COMPANY_API_KEY")})
|
|
364
499
|
${pc.cyan("--api-key-env <name>")} Custom env var name for the Token Company API key
|
|
@@ -373,4 +508,4 @@ ${pc.dim("Options:")}
|
|
|
373
508
|
}
|
|
374
509
|
|
|
375
510
|
//#endregion
|
|
376
|
-
export { compactAgentDocs, parseAgentCompactArgs, printAgentCompactHelp };
|
|
511
|
+
export { compactAgentDocs, inspectAgentCompactionState, parseAgentCompactArgs, printAgentCompactHelp, scanDocsPageTargets };
|
|
@@ -1,5 +1,314 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as renderDocsRelatedMarkdownLines } from "./search-Cu_pxL8o.mjs";
|
|
2
|
+
import matter from "gray-matter";
|
|
2
3
|
|
|
4
|
+
//#region src/define-docs.ts
|
|
5
|
+
/**
|
|
6
|
+
* Define docs configuration. Validates and returns the config.
|
|
7
|
+
*/
|
|
8
|
+
function defineDocs(config) {
|
|
9
|
+
return {
|
|
10
|
+
entry: config.entry ?? "docs",
|
|
11
|
+
contentDir: config.contentDir,
|
|
12
|
+
i18n: config.i18n,
|
|
13
|
+
theme: config.theme,
|
|
14
|
+
nav: config.nav,
|
|
15
|
+
github: config.github,
|
|
16
|
+
themeToggle: config.themeToggle,
|
|
17
|
+
breadcrumb: config.breadcrumb,
|
|
18
|
+
sidebar: config.sidebar,
|
|
19
|
+
components: config.components,
|
|
20
|
+
onCopyClick: config.onCopyClick,
|
|
21
|
+
feedback: config.feedback,
|
|
22
|
+
search: config.search,
|
|
23
|
+
mcp: config.mcp,
|
|
24
|
+
icons: config.icons,
|
|
25
|
+
pageActions: config.pageActions,
|
|
26
|
+
lastUpdated: config.lastUpdated,
|
|
27
|
+
readingTime: config.readingTime,
|
|
28
|
+
llmsTxt: config.llmsTxt,
|
|
29
|
+
ai: config.ai,
|
|
30
|
+
ordering: config.ordering,
|
|
31
|
+
metadata: config.metadata,
|
|
32
|
+
og: config.og,
|
|
33
|
+
changelog: config.changelog,
|
|
34
|
+
apiReference: config.apiReference,
|
|
35
|
+
agent: config.agent
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/changelog.ts
|
|
41
|
+
function normalizePathSegment(value, fallback) {
|
|
42
|
+
return (value ?? fallback).trim().replace(/^\/+|\/+$/g, "") || fallback;
|
|
43
|
+
}
|
|
44
|
+
function normalizeContentDir(value) {
|
|
45
|
+
const trimmed = value?.trim();
|
|
46
|
+
if (!trimmed) return "changelog";
|
|
47
|
+
return trimmed.replace(/\/+$/, "") || "changelog";
|
|
48
|
+
}
|
|
49
|
+
function resolveChangelogConfig(value) {
|
|
50
|
+
if (value === false || value === void 0) return {
|
|
51
|
+
enabled: false,
|
|
52
|
+
path: "changelog",
|
|
53
|
+
contentDir: "changelog",
|
|
54
|
+
title: "Changelog",
|
|
55
|
+
description: void 0,
|
|
56
|
+
search: true
|
|
57
|
+
};
|
|
58
|
+
if (value === true) return {
|
|
59
|
+
enabled: true,
|
|
60
|
+
path: "changelog",
|
|
61
|
+
contentDir: "changelog",
|
|
62
|
+
title: "Changelog",
|
|
63
|
+
description: void 0,
|
|
64
|
+
search: true
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
enabled: value.enabled !== false,
|
|
68
|
+
path: normalizePathSegment(value.path, "changelog"),
|
|
69
|
+
contentDir: normalizeContentDir(value.contentDir),
|
|
70
|
+
title: value.title?.trim() || "Changelog",
|
|
71
|
+
description: value.description?.trim() || void 0,
|
|
72
|
+
search: value.search !== false,
|
|
73
|
+
actionsComponent: value.actionsComponent
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/utils.ts
|
|
79
|
+
/**
|
|
80
|
+
* Deep merge utility for theme overrides.
|
|
81
|
+
* Merges objects recursively; later values override earlier ones.
|
|
82
|
+
*/
|
|
83
|
+
function deepMerge(target, ...sources) {
|
|
84
|
+
if (!sources.length) return target;
|
|
85
|
+
const source = sources.shift();
|
|
86
|
+
if (!source) return target;
|
|
87
|
+
const result = { ...target };
|
|
88
|
+
for (const key of Object.keys(source)) {
|
|
89
|
+
const sourceVal = source[key];
|
|
90
|
+
const targetVal = result[key];
|
|
91
|
+
if (sourceVal && typeof sourceVal === "object" && !Array.isArray(sourceVal) && targetVal && typeof targetVal === "object" && !Array.isArray(targetVal)) result[key] = deepMerge(targetVal, sourceVal);
|
|
92
|
+
else if (sourceVal !== void 0) result[key] = sourceVal;
|
|
93
|
+
}
|
|
94
|
+
if (sources.length) return deepMerge(result, ...sources);
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/create-theme.ts
|
|
100
|
+
/**
|
|
101
|
+
* Create a theme preset factory.
|
|
102
|
+
*
|
|
103
|
+
* Returns a function that accepts optional overrides and deep-merges them
|
|
104
|
+
* with the base theme defaults. This is the same pattern used by the
|
|
105
|
+
* built-in `fumadocs()`, `darksharp()`, and `pixelBorder()` presets.
|
|
106
|
+
*
|
|
107
|
+
* @param baseTheme - The default theme configuration
|
|
108
|
+
* @returns A factory function `(overrides?) => DocsTheme`
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* import { createTheme } from "@farming-labs/docs";
|
|
113
|
+
*
|
|
114
|
+
* export const myTheme = createTheme({
|
|
115
|
+
* name: "my-theme",
|
|
116
|
+
* ui: {
|
|
117
|
+
* colors: { primary: "#6366f1" },
|
|
118
|
+
* layout: { contentWidth: 800 },
|
|
119
|
+
* },
|
|
120
|
+
* });
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
function createTheme(baseTheme) {
|
|
124
|
+
return function themeFactory(overrides = {}) {
|
|
125
|
+
const merged = deepMerge(baseTheme, overrides);
|
|
126
|
+
if (overrides.ui?.colors) merged._userColorOverrides = { ...overrides.ui.colors };
|
|
127
|
+
return merged;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Extend an existing theme preset with additional defaults.
|
|
132
|
+
*
|
|
133
|
+
* Useful when you want to build on top of an existing theme (e.g. fumadocs)
|
|
134
|
+
* rather than starting from scratch.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* import { extendTheme } from "@farming-labs/docs";
|
|
139
|
+
* import { fumadocs } from "@farming-labs/theme/default";
|
|
140
|
+
*
|
|
141
|
+
* // Start with fumadocs defaults, override some values
|
|
142
|
+
* export const myTheme = extendTheme(fumadocs(), {
|
|
143
|
+
* name: "my-custom-fumadocs",
|
|
144
|
+
* ui: { colors: { primary: "#22c55e" } },
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
function extendTheme(baseTheme, extensions) {
|
|
149
|
+
return deepMerge(baseTheme, extensions);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/i18n.ts
|
|
154
|
+
function normalizeSegment(value) {
|
|
155
|
+
return value.replace(/^\/+|\/+$/g, "");
|
|
156
|
+
}
|
|
157
|
+
function splitSegments(value) {
|
|
158
|
+
const cleaned = normalizeSegment(value);
|
|
159
|
+
return cleaned ? cleaned.split("/").filter(Boolean) : [];
|
|
160
|
+
}
|
|
161
|
+
function resolveDocsI18n(config) {
|
|
162
|
+
if (!config || !Array.isArray(config.locales)) return null;
|
|
163
|
+
const locales = Array.from(new Set(config.locales.map((l) => l.trim()).filter(Boolean)));
|
|
164
|
+
if (locales.length === 0) return null;
|
|
165
|
+
return {
|
|
166
|
+
locales,
|
|
167
|
+
defaultLocale: config.defaultLocale && locales.includes(config.defaultLocale) ? config.defaultLocale : locales[0]
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function resolveDocsLocale(searchParams, i18n) {
|
|
171
|
+
if (!i18n) return void 0;
|
|
172
|
+
const raw = searchParams.get("lang") ?? searchParams.get("locale");
|
|
173
|
+
if (!raw) return void 0;
|
|
174
|
+
if (i18n.locales.includes(raw)) return raw;
|
|
175
|
+
return i18n.defaultLocale;
|
|
176
|
+
}
|
|
177
|
+
function resolveDocsPath(pathname, entry) {
|
|
178
|
+
const entryBase = normalizeSegment(entry || "docs") || "docs";
|
|
179
|
+
const entryParts = splitSegments(entryBase);
|
|
180
|
+
const pathParts = splitSegments(pathname);
|
|
181
|
+
let rest = pathParts;
|
|
182
|
+
if (entryParts.length > 0) {
|
|
183
|
+
if (pathParts.slice(0, entryParts.length).join("/") === entryParts.join("/")) rest = pathParts.slice(entryParts.length);
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
slug: rest.join("/"),
|
|
187
|
+
entryPath: entryBase
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/metadata.ts
|
|
193
|
+
/**
|
|
194
|
+
* Resolve page title using metadata titleTemplate.
|
|
195
|
+
* %s is replaced with page title.
|
|
196
|
+
*/
|
|
197
|
+
function resolveTitle(pageTitle, metadata) {
|
|
198
|
+
return (metadata?.titleTemplate ?? "%s").replace("%s", pageTitle);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Resolve OG image URL for a page.
|
|
202
|
+
* Prefers page.openGraph.images[0], then page.ogImage, then config endpoint/default.
|
|
203
|
+
*/
|
|
204
|
+
function resolveOGImage(page, ogConfig, baseUrl) {
|
|
205
|
+
if (page.openGraph?.images?.length) return resolveImageUrl(page.openGraph.images[0].url, baseUrl);
|
|
206
|
+
if (!ogConfig?.enabled) return void 0;
|
|
207
|
+
if (page.ogImage) return resolveImageUrl(page.ogImage, baseUrl);
|
|
208
|
+
if (ogConfig.type === "dynamic" && ogConfig.endpoint) return `${baseUrl ?? ""}${ogConfig.endpoint}`;
|
|
209
|
+
return ogConfig.defaultImage;
|
|
210
|
+
}
|
|
211
|
+
function resolveImageUrl(url, baseUrl) {
|
|
212
|
+
if (url.startsWith("/") || url.startsWith("http")) return url;
|
|
213
|
+
const base = baseUrl ?? "";
|
|
214
|
+
return `${base}${base.length > 0 && !base.endsWith("/") ? "/" : ""}${url}`;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Build the Open Graph metadata object for a page.
|
|
218
|
+
* When the page has openGraph in frontmatter, uses it (with title/description filled from page if omitted).
|
|
219
|
+
* Otherwise uses ogImage or config (dynamic endpoint / defaultImage).
|
|
220
|
+
*/
|
|
221
|
+
function buildPageOpenGraph(page, ogConfig, baseUrl) {
|
|
222
|
+
if (page.openGraph) {
|
|
223
|
+
const images = page.openGraph.images?.length ? page.openGraph.images.map((img) => ({
|
|
224
|
+
url: resolveImageUrl(img.url, baseUrl),
|
|
225
|
+
width: img.width ?? 1200,
|
|
226
|
+
height: img.height ?? 630
|
|
227
|
+
})) : void 0;
|
|
228
|
+
return {
|
|
229
|
+
title: page.openGraph.title ?? page.title,
|
|
230
|
+
description: page.openGraph.description ?? page.description,
|
|
231
|
+
...images && { images }
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const url = resolveOGImage(page, ogConfig, baseUrl);
|
|
235
|
+
if (!url) return void 0;
|
|
236
|
+
return {
|
|
237
|
+
title: page.title,
|
|
238
|
+
...page.description && { description: page.description },
|
|
239
|
+
images: [{
|
|
240
|
+
url,
|
|
241
|
+
width: 1200,
|
|
242
|
+
height: 630
|
|
243
|
+
}]
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Build the Twitter card metadata object for a page.
|
|
248
|
+
* When the page has twitter in frontmatter, uses it.
|
|
249
|
+
* Otherwise builds from ogImage or config (dynamic endpoint).
|
|
250
|
+
*/
|
|
251
|
+
function buildPageTwitter(page, ogConfig, baseUrl) {
|
|
252
|
+
if (page.twitter) {
|
|
253
|
+
const images = page.twitter.images?.length ? page.twitter.images.map((url) => resolveImageUrl(url, baseUrl)) : void 0;
|
|
254
|
+
return {
|
|
255
|
+
...page.twitter.card && { card: page.twitter.card },
|
|
256
|
+
...page.twitter.title !== void 0 && { title: page.twitter.title },
|
|
257
|
+
...page.twitter.description !== void 0 && { description: page.twitter.description },
|
|
258
|
+
...images && { images }
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const url = resolveOGImage(page, ogConfig, baseUrl);
|
|
262
|
+
if (!url) return void 0;
|
|
263
|
+
return {
|
|
264
|
+
card: "summary_large_image",
|
|
265
|
+
title: page.title,
|
|
266
|
+
...page.description && { description: page.description },
|
|
267
|
+
images: [url]
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/reading-time.ts
|
|
273
|
+
function hasExplicitReadingTime(frontmatter) {
|
|
274
|
+
return Object.prototype.hasOwnProperty.call(frontmatter ?? {}, "readingTime");
|
|
275
|
+
}
|
|
276
|
+
function normalizeWordsPerMinute(wordsPerMinute) {
|
|
277
|
+
if (typeof wordsPerMinute !== "number" || !Number.isFinite(wordsPerMinute)) return 220;
|
|
278
|
+
return Math.max(1, Math.floor(wordsPerMinute));
|
|
279
|
+
}
|
|
280
|
+
function stripNonReadingContent(content) {
|
|
281
|
+
return content.replace(/(`{3,})[^\n]*\n[\s\S]*?\1/g, " ").replace(/(~{3,})[^\n]*\n[\s\S]*?\1/g, " ").replace(/`[^`\n]+`/g, " ").replace(/!\[[^\]]*\]\([^)]+\)/g, " ").replace(/\[([^\]]+)\]\([^)]+\)/g, " $1 ").replace(/<[^>]+>/g, " ").replace(/\{[^{}]*\}/g, " ").replace(/https?:\/\/\S+/g, " ").replace(/[#>*_~|]/g, " ");
|
|
282
|
+
}
|
|
283
|
+
function estimateReadingTimeMinutes(content, wordsPerMinute) {
|
|
284
|
+
const wordCount = stripNonReadingContent(content).match(/\b[\p{L}\p{N}][\p{L}\p{N}'’-]*\b/gu)?.length ?? 0;
|
|
285
|
+
return Math.max(1, Math.ceil(wordCount / normalizeWordsPerMinute(wordsPerMinute)));
|
|
286
|
+
}
|
|
287
|
+
function resolveReadingTimeOptions(readingTime) {
|
|
288
|
+
if (readingTime === true) return { enabled: true };
|
|
289
|
+
if (readingTime === false || readingTime === void 0 || readingTime === null) return { enabled: false };
|
|
290
|
+
if (typeof readingTime !== "object") return { enabled: false };
|
|
291
|
+
return {
|
|
292
|
+
enabled: readingTime.enabled !== false,
|
|
293
|
+
wordsPerMinute: typeof readingTime.wordsPerMinute === "number" && Number.isFinite(readingTime.wordsPerMinute) ? readingTime.wordsPerMinute : void 0
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function resolveReadingTimeFromContent(frontmatter, content, wordsPerMinute) {
|
|
297
|
+
const pageData = frontmatter ?? {};
|
|
298
|
+
if (pageData.readingTime === false) return null;
|
|
299
|
+
if (typeof pageData.readingTime === "number" && Number.isFinite(pageData.readingTime)) return Math.max(1, Math.ceil(pageData.readingTime));
|
|
300
|
+
return estimateReadingTimeMinutes(content, wordsPerMinute);
|
|
301
|
+
}
|
|
302
|
+
function resolvePageReadingTime(frontmatter, content, options) {
|
|
303
|
+
if (!(options?.enabledByDefault ?? false) && !hasExplicitReadingTime(frontmatter)) return;
|
|
304
|
+
return resolveReadingTimeFromContent(frontmatter, content, options?.wordsPerMinute);
|
|
305
|
+
}
|
|
306
|
+
function resolveReadingTimeFromSource(source, wordsPerMinute) {
|
|
307
|
+
const { data, content } = matter(source);
|
|
308
|
+
return resolveReadingTimeFromContent(data, content, wordsPerMinute);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
//#endregion
|
|
3
312
|
//#region src/agent.ts
|
|
4
313
|
const DEFAULT_DOCS_API_ROUTE = "/api/docs";
|
|
5
314
|
const DEFAULT_AGENT_SPEC_ROUTE = "/api/docs/agent/spec";
|
|
@@ -318,4 +627,4 @@ function toYamlString(value) {
|
|
|
318
627
|
}
|
|
319
628
|
|
|
320
629
|
//#endregion
|
|
321
|
-
export { renderDocsMarkdownDocument as C, resolveDocsMarkdownRequest as D, resolveDocsLlmsTxtFormat as E, resolveDocsSkillFormat as O, normalizeDocsUrlPath as S, resolveDocsAgentMdxContent as T, isDocsAgentDiscoveryRequest as _, DEFAULT_DOCS_API_ROUTE as a, isDocsSkillRequest as b, DEFAULT_LLMS_TXT_ROUTE as c, DEFAULT_MCP_ROUTE as d, DEFAULT_MCP_WELL_KNOWN_ROUTE as f, findDocsMarkdownPage as g, buildDocsAgentDiscoverySpec as h, DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE as i, DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE as l, DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE as m, DEFAULT_AGENT_SPEC_ROUTE as n, DEFAULT_LLMS_FULL_TXT_ROUTE as o, DEFAULT_SKILL_MD_ROUTE as p, DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE as r, DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE as s, DEFAULT_AGENT_FEEDBACK_ROUTE as t, DEFAULT_MCP_PUBLIC_ROUTE as u, isDocsMcpRequest as v, renderDocsSkillDocument as w, normalizeDocsPathSegment as x, isDocsPublicGetRequest as y };
|
|
630
|
+
export { resolvePageReadingTime as A, resolveDocsPath as B, renderDocsMarkdownDocument as C, resolveDocsMarkdownRequest as D, resolveDocsLlmsTxtFormat as E, buildPageTwitter as F, defineDocs as G, extendTheme as H, resolveOGImage as I, resolveTitle as L, resolveReadingTimeFromSource as M, resolveReadingTimeOptions as N, resolveDocsSkillFormat as O, buildPageOpenGraph as P, resolveDocsI18n as R, normalizeDocsUrlPath as S, resolveDocsAgentMdxContent as T, deepMerge as U, createTheme as V, resolveChangelogConfig as W, isDocsAgentDiscoveryRequest as _, DEFAULT_DOCS_API_ROUTE as a, isDocsSkillRequest as b, DEFAULT_LLMS_TXT_ROUTE as c, DEFAULT_MCP_ROUTE as d, DEFAULT_MCP_WELL_KNOWN_ROUTE as f, findDocsMarkdownPage as g, buildDocsAgentDiscoverySpec as h, DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE as i, resolveReadingTimeFromContent as j, estimateReadingTimeMinutes as k, DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE as l, DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE as m, DEFAULT_AGENT_SPEC_ROUTE as n, DEFAULT_LLMS_FULL_TXT_ROUTE as o, DEFAULT_SKILL_MD_ROUTE as p, DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE as r, DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE as s, DEFAULT_AGENT_FEEDBACK_ROUTE as t, DEFAULT_MCP_PUBLIC_ROUTE as u, isDocsMcpRequest as v, renderDocsSkillDocument as w, normalizeDocsPathSegment as x, isDocsPublicGetRequest as y, resolveDocsLocale as z };
|
package/dist/cli/index.mjs
CHANGED
|
@@ -69,13 +69,13 @@ async function main() {
|
|
|
69
69
|
searchApiKey: typeof flags["search-api-key"] === "string" ? flags["search-api-key"] : void 0
|
|
70
70
|
};
|
|
71
71
|
if (!parsedCommand.command || parsedCommand.command === "init") {
|
|
72
|
-
const { init } = await import("../init-
|
|
72
|
+
const { init } = await import("../init-C1tsFxXo.mjs");
|
|
73
73
|
await init(initOptions);
|
|
74
74
|
} else if (parsedCommand.command === "mcp") {
|
|
75
|
-
const { runMcp } = await import("../mcp-
|
|
75
|
+
const { runMcp } = await import("../mcp-C-TmMrdw.mjs");
|
|
76
76
|
await runMcp(mcpOptions);
|
|
77
77
|
} else if (parsedCommand.command === "agent" && subcommand === "compact") {
|
|
78
|
-
const { compactAgentDocs, parseAgentCompactArgs, printAgentCompactHelp } = await import("../agent-
|
|
78
|
+
const { compactAgentDocs, parseAgentCompactArgs, printAgentCompactHelp } = await import("../agent-C3rj3o1l.mjs");
|
|
79
79
|
const agentCompactOptions = parseAgentCompactArgs(args.slice(2));
|
|
80
80
|
if (agentCompactOptions.help) {
|
|
81
81
|
printAgentCompactHelp();
|
|
@@ -85,11 +85,11 @@ async function main() {
|
|
|
85
85
|
} else if (parsedCommand.command === "agent") {
|
|
86
86
|
console.error(pc.red(`Unknown agent subcommand: ${subcommand ?? "(missing)"}`));
|
|
87
87
|
console.error();
|
|
88
|
-
const { printAgentCompactHelp } = await import("../agent-
|
|
88
|
+
const { printAgentCompactHelp } = await import("../agent-C3rj3o1l.mjs");
|
|
89
89
|
printAgentCompactHelp();
|
|
90
90
|
process.exit(1);
|
|
91
91
|
} else if (parsedCommand.command === "doctor") {
|
|
92
|
-
const { parseDoctorArgs, printDoctorHelp, runDoctor } = await import("../doctor-
|
|
92
|
+
const { parseDoctorArgs, printDoctorHelp, runDoctor } = await import("../doctor-B0xEGzBU.mjs");
|
|
93
93
|
const doctorOptions = parseDoctorArgs(args.slice(1));
|
|
94
94
|
if (doctorOptions.help) {
|
|
95
95
|
printDoctorHelp();
|
|
@@ -97,7 +97,7 @@ async function main() {
|
|
|
97
97
|
}
|
|
98
98
|
await runDoctor(doctorOptions);
|
|
99
99
|
} else if (parsedCommand.command === "search" && subcommand === "sync") {
|
|
100
|
-
const { syncSearch } = await import("../search-
|
|
100
|
+
const { syncSearch } = await import("../search-C7mKYgqQ.mjs");
|
|
101
101
|
await syncSearch(searchSyncOptions);
|
|
102
102
|
} else if (parsedCommand.command === "search") {
|
|
103
103
|
console.error(pc.red(`Unknown search subcommand: ${subcommand ?? "(missing)"}`));
|
|
@@ -105,7 +105,7 @@ async function main() {
|
|
|
105
105
|
printHelp();
|
|
106
106
|
process.exit(1);
|
|
107
107
|
} else if (parsedCommand.command === "upgrade") {
|
|
108
|
-
const { upgrade } = await import("../upgrade-
|
|
108
|
+
const { upgrade } = await import("../upgrade-a185_G5A.mjs");
|
|
109
109
|
await upgrade({
|
|
110
110
|
framework: (typeof flags.framework === "string" ? flags.framework : void 0) ?? (args[1] && !args[1].startsWith("--") ? args[1] : void 0),
|
|
111
111
|
tag: args.includes("--beta") ? "beta" : args.includes("--latest") ? "latest" : parsedCommand.tag ?? "latest"
|
|
@@ -152,7 +152,9 @@ ${pc.dim("Options for mcp:")}
|
|
|
152
152
|
${pc.dim("Options for agent compact:")}
|
|
153
153
|
${pc.cyan("agent compact <page...>")} Compact pages and write sibling ${pc.dim("agent.md")} files
|
|
154
154
|
${pc.cyan("agent compact --all")} Compact every folder-based docs page
|
|
155
|
+
${pc.cyan("agent compact --stale")} Refresh only stale generated ${pc.dim("agent.md")} files
|
|
155
156
|
${pc.cyan("--page <slug|path>")} Repeatable explicit page flag; positional page args work too
|
|
157
|
+
${pc.cyan("--include-missing")} With ${pc.cyan("--stale")}, also create explicit or token-budget pages missing ${pc.dim("agent.md")}
|
|
156
158
|
${pc.cyan("--api-key <key>")} Token Company API key (or use ${pc.dim("TOKEN_COMPANY_API_KEY")})
|
|
157
159
|
${pc.cyan("--api-key-env <name>")} Custom env var name for the Token Company API key
|
|
158
160
|
${pc.cyan("--base-url <url>")} Override the Token Company API base URL
|
|
@@ -164,6 +166,7 @@ ${pc.dim("Options for doctor:")}
|
|
|
164
166
|
${pc.cyan("doctor --agent")} Same as ${pc.cyan("doctor")}; explicit agent scoring mode
|
|
165
167
|
${pc.cyan("doctor --site")} Score the current docs app for reader-facing docs quality
|
|
166
168
|
${pc.cyan("doctor --human")} Alias for ${pc.cyan("doctor --site")}
|
|
169
|
+
${pc.cyan("doctor --json")} Print the report as JSON for CI, scripts, and automation
|
|
167
170
|
${pc.cyan("doctor agent")} Subcommand alias for agent scoring
|
|
168
171
|
${pc.cyan("doctor site")} Subcommand alias for reader-facing scoring
|
|
169
172
|
${pc.cyan("doctor human")} Legacy alias for reader-facing scoring
|