@farming-labs/docs 0.1.40 → 0.1.42

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.
@@ -0,0 +1,588 @@
1
+ import { u as renderDocsRelatedMarkdownLines } from "./search-8oEskRtz.mjs";
2
+
3
+ //#region src/define-docs.ts
4
+ /**
5
+ * Define docs configuration. Validates and returns the config.
6
+ */
7
+ function defineDocs(config) {
8
+ return {
9
+ entry: config.entry ?? "docs",
10
+ contentDir: config.contentDir,
11
+ i18n: config.i18n,
12
+ theme: config.theme,
13
+ nav: config.nav,
14
+ github: config.github,
15
+ themeToggle: config.themeToggle,
16
+ breadcrumb: config.breadcrumb,
17
+ sidebar: config.sidebar,
18
+ components: config.components,
19
+ onCopyClick: config.onCopyClick,
20
+ feedback: config.feedback,
21
+ search: config.search,
22
+ mcp: config.mcp,
23
+ icons: config.icons,
24
+ pageActions: config.pageActions,
25
+ lastUpdated: config.lastUpdated,
26
+ llmsTxt: config.llmsTxt,
27
+ ai: config.ai,
28
+ ordering: config.ordering,
29
+ metadata: config.metadata,
30
+ og: config.og,
31
+ changelog: config.changelog,
32
+ apiReference: config.apiReference,
33
+ agent: config.agent
34
+ };
35
+ }
36
+
37
+ //#endregion
38
+ //#region src/changelog.ts
39
+ function normalizePathSegment(value, fallback) {
40
+ return (value ?? fallback).trim().replace(/^\/+|\/+$/g, "") || fallback;
41
+ }
42
+ function normalizeContentDir(value) {
43
+ const trimmed = value?.trim();
44
+ if (!trimmed) return "changelog";
45
+ return trimmed.replace(/\/+$/, "") || "changelog";
46
+ }
47
+ function resolveChangelogConfig(value) {
48
+ if (value === false || value === void 0) return {
49
+ enabled: false,
50
+ path: "changelog",
51
+ contentDir: "changelog",
52
+ title: "Changelog",
53
+ description: void 0,
54
+ search: true
55
+ };
56
+ if (value === true) return {
57
+ enabled: true,
58
+ path: "changelog",
59
+ contentDir: "changelog",
60
+ title: "Changelog",
61
+ description: void 0,
62
+ search: true
63
+ };
64
+ return {
65
+ enabled: value.enabled !== false,
66
+ path: normalizePathSegment(value.path, "changelog"),
67
+ contentDir: normalizeContentDir(value.contentDir),
68
+ title: value.title?.trim() || "Changelog",
69
+ description: value.description?.trim() || void 0,
70
+ search: value.search !== false,
71
+ actionsComponent: value.actionsComponent
72
+ };
73
+ }
74
+
75
+ //#endregion
76
+ //#region src/utils.ts
77
+ /**
78
+ * Deep merge utility for theme overrides.
79
+ * Merges objects recursively; later values override earlier ones.
80
+ */
81
+ function deepMerge(target, ...sources) {
82
+ if (!sources.length) return target;
83
+ const source = sources.shift();
84
+ if (!source) return target;
85
+ const result = { ...target };
86
+ for (const key of Object.keys(source)) {
87
+ const sourceVal = source[key];
88
+ const targetVal = result[key];
89
+ if (sourceVal && typeof sourceVal === "object" && !Array.isArray(sourceVal) && targetVal && typeof targetVal === "object" && !Array.isArray(targetVal)) result[key] = deepMerge(targetVal, sourceVal);
90
+ else if (sourceVal !== void 0) result[key] = sourceVal;
91
+ }
92
+ if (sources.length) return deepMerge(result, ...sources);
93
+ return result;
94
+ }
95
+
96
+ //#endregion
97
+ //#region src/create-theme.ts
98
+ /**
99
+ * Create a theme preset factory.
100
+ *
101
+ * Returns a function that accepts optional overrides and deep-merges them
102
+ * with the base theme defaults. This is the same pattern used by the
103
+ * built-in `fumadocs()`, `darksharp()`, and `pixelBorder()` presets.
104
+ *
105
+ * @param baseTheme - The default theme configuration
106
+ * @returns A factory function `(overrides?) => DocsTheme`
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * import { createTheme } from "@farming-labs/docs";
111
+ *
112
+ * export const myTheme = createTheme({
113
+ * name: "my-theme",
114
+ * ui: {
115
+ * colors: { primary: "#6366f1" },
116
+ * layout: { contentWidth: 800 },
117
+ * },
118
+ * });
119
+ * ```
120
+ */
121
+ function createTheme(baseTheme) {
122
+ return function themeFactory(overrides = {}) {
123
+ const merged = deepMerge(baseTheme, overrides);
124
+ if (overrides.ui?.colors) merged._userColorOverrides = { ...overrides.ui.colors };
125
+ return merged;
126
+ };
127
+ }
128
+ /**
129
+ * Extend an existing theme preset with additional defaults.
130
+ *
131
+ * Useful when you want to build on top of an existing theme (e.g. fumadocs)
132
+ * rather than starting from scratch.
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * import { extendTheme } from "@farming-labs/docs";
137
+ * import { fumadocs } from "@farming-labs/theme/default";
138
+ *
139
+ * // Start with fumadocs defaults, override some values
140
+ * export const myTheme = extendTheme(fumadocs(), {
141
+ * name: "my-custom-fumadocs",
142
+ * ui: { colors: { primary: "#22c55e" } },
143
+ * });
144
+ * ```
145
+ */
146
+ function extendTheme(baseTheme, extensions) {
147
+ return deepMerge(baseTheme, extensions);
148
+ }
149
+
150
+ //#endregion
151
+ //#region src/i18n.ts
152
+ function normalizeSegment(value) {
153
+ return value.replace(/^\/+|\/+$/g, "");
154
+ }
155
+ function splitSegments(value) {
156
+ const cleaned = normalizeSegment(value);
157
+ return cleaned ? cleaned.split("/").filter(Boolean) : [];
158
+ }
159
+ function resolveDocsI18n(config) {
160
+ if (!config || !Array.isArray(config.locales)) return null;
161
+ const locales = Array.from(new Set(config.locales.map((l) => l.trim()).filter(Boolean)));
162
+ if (locales.length === 0) return null;
163
+ return {
164
+ locales,
165
+ defaultLocale: config.defaultLocale && locales.includes(config.defaultLocale) ? config.defaultLocale : locales[0]
166
+ };
167
+ }
168
+ function resolveDocsLocale(searchParams, i18n) {
169
+ if (!i18n) return void 0;
170
+ const raw = searchParams.get("lang") ?? searchParams.get("locale");
171
+ if (!raw) return void 0;
172
+ if (i18n.locales.includes(raw)) return raw;
173
+ return i18n.defaultLocale;
174
+ }
175
+ function resolveDocsPath(pathname, entry) {
176
+ const entryBase = normalizeSegment(entry || "docs") || "docs";
177
+ const entryParts = splitSegments(entryBase);
178
+ const pathParts = splitSegments(pathname);
179
+ let rest = pathParts;
180
+ if (entryParts.length > 0) {
181
+ if (pathParts.slice(0, entryParts.length).join("/") === entryParts.join("/")) rest = pathParts.slice(entryParts.length);
182
+ }
183
+ return {
184
+ slug: rest.join("/"),
185
+ entryPath: entryBase
186
+ };
187
+ }
188
+
189
+ //#endregion
190
+ //#region src/metadata.ts
191
+ /**
192
+ * Resolve page title using metadata titleTemplate.
193
+ * %s is replaced with page title.
194
+ */
195
+ function resolveTitle(pageTitle, metadata) {
196
+ return (metadata?.titleTemplate ?? "%s").replace("%s", pageTitle);
197
+ }
198
+ /**
199
+ * Resolve OG image URL for a page.
200
+ * Prefers page.openGraph.images[0], then page.ogImage, then config endpoint/default.
201
+ */
202
+ function resolveOGImage(page, ogConfig, baseUrl) {
203
+ if (page.openGraph?.images?.length) return resolveImageUrl(page.openGraph.images[0].url, baseUrl);
204
+ if (!ogConfig?.enabled) return void 0;
205
+ if (page.ogImage) return resolveImageUrl(page.ogImage, baseUrl);
206
+ if (ogConfig.type === "dynamic" && ogConfig.endpoint) return `${baseUrl ?? ""}${ogConfig.endpoint}`;
207
+ return ogConfig.defaultImage;
208
+ }
209
+ function resolveImageUrl(url, baseUrl) {
210
+ if (url.startsWith("/") || url.startsWith("http")) return url;
211
+ const base = baseUrl ?? "";
212
+ return `${base}${base.length > 0 && !base.endsWith("/") ? "/" : ""}${url}`;
213
+ }
214
+ /**
215
+ * Build the Open Graph metadata object for a page.
216
+ * When the page has openGraph in frontmatter, uses it (with title/description filled from page if omitted).
217
+ * Otherwise uses ogImage or config (dynamic endpoint / defaultImage).
218
+ */
219
+ function buildPageOpenGraph(page, ogConfig, baseUrl) {
220
+ if (page.openGraph) {
221
+ const images = page.openGraph.images?.length ? page.openGraph.images.map((img) => ({
222
+ url: resolveImageUrl(img.url, baseUrl),
223
+ width: img.width ?? 1200,
224
+ height: img.height ?? 630
225
+ })) : void 0;
226
+ return {
227
+ title: page.openGraph.title ?? page.title,
228
+ description: page.openGraph.description ?? page.description,
229
+ ...images && { images }
230
+ };
231
+ }
232
+ const url = resolveOGImage(page, ogConfig, baseUrl);
233
+ if (!url) return void 0;
234
+ return {
235
+ title: page.title,
236
+ ...page.description && { description: page.description },
237
+ images: [{
238
+ url,
239
+ width: 1200,
240
+ height: 630
241
+ }]
242
+ };
243
+ }
244
+ /**
245
+ * Build the Twitter card metadata object for a page.
246
+ * When the page has twitter in frontmatter, uses it.
247
+ * Otherwise builds from ogImage or config (dynamic endpoint).
248
+ */
249
+ function buildPageTwitter(page, ogConfig, baseUrl) {
250
+ if (page.twitter) {
251
+ const images = page.twitter.images?.length ? page.twitter.images.map((url) => resolveImageUrl(url, baseUrl)) : void 0;
252
+ return {
253
+ ...page.twitter.card && { card: page.twitter.card },
254
+ ...page.twitter.title !== void 0 && { title: page.twitter.title },
255
+ ...page.twitter.description !== void 0 && { description: page.twitter.description },
256
+ ...images && { images }
257
+ };
258
+ }
259
+ const url = resolveOGImage(page, ogConfig, baseUrl);
260
+ if (!url) return void 0;
261
+ return {
262
+ card: "summary_large_image",
263
+ title: page.title,
264
+ ...page.description && { description: page.description },
265
+ images: [url]
266
+ };
267
+ }
268
+
269
+ //#endregion
270
+ //#region src/agent.ts
271
+ const DEFAULT_DOCS_API_ROUTE = "/api/docs";
272
+ const DEFAULT_AGENT_SPEC_ROUTE = "/api/docs/agent/spec";
273
+ const DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE = "/.well-known/agent";
274
+ const DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE = "/.well-known/agent.json";
275
+ const DEFAULT_MCP_ROUTE = "/api/docs/mcp";
276
+ const DEFAULT_MCP_PUBLIC_ROUTE = "/mcp";
277
+ const DEFAULT_MCP_WELL_KNOWN_ROUTE = "/.well-known/mcp";
278
+ const DEFAULT_LLMS_TXT_ROUTE = "/llms.txt";
279
+ const DEFAULT_LLMS_FULL_TXT_ROUTE = "/llms-full.txt";
280
+ const DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms.txt";
281
+ const DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE = "/.well-known/llms-full.txt";
282
+ const DEFAULT_SKILL_MD_ROUTE = "/skill.md";
283
+ const DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE = "/.well-known/skill.md";
284
+ const DEFAULT_AGENT_FEEDBACK_ROUTE = "/api/docs/agent/feedback";
285
+ function normalizeDocsPathSegment(value) {
286
+ return value.replace(/^\/+|\/+$/g, "");
287
+ }
288
+ function normalizeDocsUrlPath(value) {
289
+ const normalized = value.replace(/\/+/g, "/");
290
+ if (normalized === "/") return normalized;
291
+ return normalized.replace(/\/+$/, "");
292
+ }
293
+ function isDocsAgentDiscoveryRequest(url) {
294
+ const pathname = normalizeDocsUrlPath(url.pathname);
295
+ if (pathname === DEFAULT_DOCS_API_ROUTE && url.searchParams.get("agent")?.trim() === "spec") return true;
296
+ return pathname === DEFAULT_AGENT_SPEC_ROUTE || pathname === DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE || pathname === DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE;
297
+ }
298
+ function isDocsMcpRequest(url) {
299
+ const pathname = normalizeDocsUrlPath(url.pathname);
300
+ return pathname === DEFAULT_MCP_ROUTE || pathname === DEFAULT_MCP_PUBLIC_ROUTE || pathname === DEFAULT_MCP_WELL_KNOWN_ROUTE;
301
+ }
302
+ function isDocsSkillRequest(url) {
303
+ const pathname = normalizeDocsUrlPath(url.pathname);
304
+ if (pathname === DEFAULT_SKILL_MD_ROUTE || pathname === DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE) return true;
305
+ return pathname === DEFAULT_DOCS_API_ROUTE && resolveDocsSkillFormat(url) === "skill";
306
+ }
307
+ function resolveDocsSkillFormat(url) {
308
+ return url.searchParams.get("format")?.trim() === "skill" ? "skill" : null;
309
+ }
310
+ function isDocsPublicGetRequest(entry, url, request) {
311
+ const pathname = normalizeDocsUrlPath(url.pathname);
312
+ if (pathname === DEFAULT_DOCS_API_ROUTE || pathname === DEFAULT_MCP_ROUTE) return false;
313
+ return isDocsAgentDiscoveryRequest(url) || isDocsSkillRequest(url) || resolveDocsLlmsTxtFormat(url) !== null || resolveDocsMarkdownRequest(entry, url, request) !== null;
314
+ }
315
+ function resolveDocsLlmsTxtFormat(url) {
316
+ const pathname = normalizeDocsUrlPath(url.pathname);
317
+ if (pathname === DEFAULT_LLMS_TXT_ROUTE || pathname === DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE) return "llms";
318
+ if (pathname === DEFAULT_LLMS_FULL_TXT_ROUTE || pathname === DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE) return "llms-full";
319
+ const format = url.searchParams.get("format");
320
+ return pathname === DEFAULT_DOCS_API_ROUTE && (format === "llms" || format === "llms-full") ? format : null;
321
+ }
322
+ function resolveDocsMarkdownRequest(entry, url, request) {
323
+ const pathname = normalizeDocsUrlPath(url.pathname);
324
+ const format = url.searchParams.get("format")?.trim();
325
+ if (pathname === DEFAULT_DOCS_API_ROUTE && format === "markdown") return { requestedPath: url.searchParams.get("path")?.trim() ?? "" };
326
+ const normalizedEntry = `/${normalizeDocsPathSegment(entry) || "docs"}`;
327
+ if (pathname === `${normalizedEntry}.md`) return { requestedPath: "" };
328
+ const slugPrefix = `${normalizedEntry}/`;
329
+ if (pathname.startsWith(slugPrefix) && pathname.endsWith(".md")) return { requestedPath: pathname.slice(slugPrefix.length, -3) };
330
+ if (acceptsMarkdown(request)) {
331
+ if (pathname === normalizedEntry) return { requestedPath: "" };
332
+ if (pathname.startsWith(slugPrefix)) return { requestedPath: pathname.slice(slugPrefix.length) };
333
+ }
334
+ return null;
335
+ }
336
+ function findDocsMarkdownPage(entry, pages, requestedPath) {
337
+ const normalizedRequest = normalizeRequestedMarkdownPath(entry, requestedPath);
338
+ for (const page of pages) if (normalizeDocsUrlPath(page.url) === normalizedRequest) return page;
339
+ const normalizedSlug = normalizeDocsPathSegment(requestedPath.replace(/^\//, "").replace(/\.md$/i, ""));
340
+ for (const page of pages) if (page.slug !== void 0 && normalizeDocsPathSegment(page.slug) === normalizedSlug) return page;
341
+ return null;
342
+ }
343
+ function renderDocsMarkdownDocument(page) {
344
+ if (page.agentRawContent !== void 0) return page.agentRawContent;
345
+ const relatedLines = renderDocsRelatedMarkdownLines(page.related);
346
+ const lines = [`# ${page.title}`, `URL: ${page.url}`];
347
+ if (page.description) lines.push(`Description: ${page.description}`);
348
+ lines.push(...relatedLines);
349
+ lines.push("", page.agentFallbackRawContent ?? page.rawContent ?? page.content);
350
+ return lines.join("\n");
351
+ }
352
+ function renderDocsSkillDocument({ origin, entry = "docs", search, mcp, feedback, llms, markdown }) {
353
+ const normalizedEntry = normalizeDocsPathSegment(entry) || "docs";
354
+ const siteTitle = compactSkillText(llms?.siteTitle ?? "Documentation");
355
+ const siteDescription = llms?.siteDescription ? compactSkillText(llms.siteDescription) : void 0;
356
+ const llmsEnabled = llms?.enabled ?? true;
357
+ const searchEnabled = isSearchEnabled(search);
358
+ const feedbackEnabled = feedback?.enabled ?? false;
359
+ const feedbackRoute = feedback?.route ?? DEFAULT_AGENT_FEEDBACK_ROUTE;
360
+ const feedbackSchemaRoute = feedback?.schemaRoute ?? `${feedbackRoute}/schema`;
361
+ const description = truncateSkillDescription(`Use ${siteTitle} through markdown routes, llms.txt, agent discovery, search, and MCP when available.`);
362
+ const markdownAcceptHeader = markdown?.acceptHeader === false ? null : "text/markdown";
363
+ const lines = [
364
+ "---",
365
+ "name: docs",
366
+ `description: ${toYamlString(description)}`,
367
+ "---",
368
+ "",
369
+ `# ${siteTitle} Skill`,
370
+ "",
371
+ `Base URL: ${origin}`
372
+ ];
373
+ if (siteDescription) lines.push(`Description: ${siteDescription}`);
374
+ lines.push("", "## When To Use", "Use this skill when you need to read or implement against this documentation site.", "", "## Start Here", `- Fetch ${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}; fall back to ${DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE} or ${DEFAULT_AGENT_SPEC_ROUTE}.`, `- Fetch /${normalizedEntry}.md for the root docs page.`, `- Fetch /${normalizedEntry}/{slug}.md for page-specific context.`);
375
+ if (markdownAcceptHeader) lines.push(`- You can also request ${markdownAcceptHeader} from normal page URLs.`);
376
+ if (searchEnabled) lines.push(`- Search with ${DEFAULT_DOCS_API_ROUTE}?query={query} when you do not know the page.`);
377
+ if (llmsEnabled) lines.push(`- Use ${DEFAULT_LLMS_TXT_ROUTE} for a compact docs index.`, `- Use ${DEFAULT_LLMS_FULL_TXT_ROUTE} for full markdown context.`);
378
+ if (mcp.enabled) lines.push(`- Use ${DEFAULT_MCP_WELL_KNOWN_ROUTE} or ${DEFAULT_MCP_PUBLIC_ROUTE} for MCP tools when your environment supports MCP.`);
379
+ if (feedbackEnabled) lines.push(`- Read ${feedbackSchemaRoute} before posting agent feedback to ${feedbackRoute}.`);
380
+ lines.push("", "## Routes", `- Skill document: ${DEFAULT_SKILL_MD_ROUTE}`, `- Skill well-known alias: ${DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE}`, `- Skill API format: ${DEFAULT_DOCS_API_ROUTE}?format=skill`, `- Agent discovery: ${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}`, `- Agent discovery fallback: ${DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE}`, `- Markdown root: /${normalizedEntry}.md`, `- Markdown pages: /${normalizedEntry}/{slug}.md`);
381
+ if (llmsEnabled) lines.push(`- llms.txt: ${DEFAULT_LLMS_TXT_ROUTE}`, `- llms-full.txt: ${DEFAULT_LLMS_FULL_TXT_ROUTE}`, `- llms well-known aliases: ${DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE}, ${DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE}`);
382
+ if (mcp.enabled) lines.push(`- MCP: ${DEFAULT_MCP_PUBLIC_ROUTE}, ${DEFAULT_MCP_WELL_KNOWN_ROUTE}`);
383
+ lines.push("", "## Reusable Framework Skills", "For framework setup, CLI, page actions, Ask AI, or configuration work, install the reusable Farming Labs skills:", "", "```sh", "npx skills add farming-labs/docs", "```");
384
+ return lines.join("\n");
385
+ }
386
+ function resolveDocsAgentMdxContent(content, audience) {
387
+ const lines = content.split("\n");
388
+ const output = [];
389
+ let fenceMarker = null;
390
+ let agentDepth = 0;
391
+ for (const line of lines) {
392
+ const trimmed = line.trim();
393
+ const fenceMatch = trimmed.match(/^(`{3,}|~{3,})/);
394
+ if (fenceMatch) {
395
+ if (!fenceMarker) fenceMarker = fenceMatch[1];
396
+ else if (trimmed.startsWith(fenceMarker)) fenceMarker = null;
397
+ if (audience === "agent" || agentDepth === 0) output.push(line);
398
+ continue;
399
+ }
400
+ if (!fenceMarker) {
401
+ if (/^<Agent(?:\s[^>]*)?\/>$/.test(trimmed)) continue;
402
+ const singleLineMatch = line.match(/^(\s*)<Agent(?:\s[^>]*)?>([\s\S]*?)<\/Agent>\s*$/);
403
+ if (singleLineMatch) {
404
+ if (audience === "agent" && singleLineMatch[2]) output.push(`${singleLineMatch[1]}${singleLineMatch[2]}`);
405
+ continue;
406
+ }
407
+ if (line.match(/^(\s*)<Agent(?:\s[^>]*)?>\s*$/)) {
408
+ agentDepth += 1;
409
+ continue;
410
+ }
411
+ const openWithContentMatch = line.match(/^(\s*)<Agent(?:\s[^>]*)?>(.*)$/);
412
+ if (openWithContentMatch) {
413
+ agentDepth += 1;
414
+ if (audience === "agent" && openWithContentMatch[2]) output.push(`${openWithContentMatch[1]}${openWithContentMatch[2]}`);
415
+ continue;
416
+ }
417
+ const closeWithContentMatch = line.match(/^(.*)<\/Agent>\s*$/);
418
+ if (closeWithContentMatch && agentDepth > 0) {
419
+ if (audience === "agent" && closeWithContentMatch[1].trim()) output.push(closeWithContentMatch[1]);
420
+ agentDepth = Math.max(0, agentDepth - 1);
421
+ continue;
422
+ }
423
+ if (/^<\/Agent>\s*$/.test(trimmed) && agentDepth > 0) {
424
+ agentDepth = Math.max(0, agentDepth - 1);
425
+ continue;
426
+ }
427
+ }
428
+ if (agentDepth > 0 && audience === "human") continue;
429
+ output.push(line);
430
+ }
431
+ return output.join("\n").replace(/\n{3,}/g, "\n\n").trim();
432
+ }
433
+ function buildDocsAgentDiscoverySpec({ origin, entry = "docs", i18n = null, search, mcp, feedback, llms, markdown }) {
434
+ const normalizedEntry = normalizeDocsPathSegment(entry) || "docs";
435
+ const localesEnabled = i18n !== null;
436
+ const searchEnabled = isSearchEnabled(search);
437
+ const feedbackRoute = feedback?.route ?? DEFAULT_AGENT_FEEDBACK_ROUTE;
438
+ const feedbackSchemaRoute = feedback?.schemaRoute ?? `${feedbackRoute}/schema`;
439
+ const llmsEnabled = llms?.enabled ?? true;
440
+ return {
441
+ version: "1",
442
+ name: "@farming-labs/docs",
443
+ baseUrl: origin,
444
+ site: {
445
+ title: llms?.siteTitle ?? "Documentation",
446
+ description: llms?.siteDescription,
447
+ entry: normalizedEntry,
448
+ baseUrl: llms?.baseUrl ?? origin
449
+ },
450
+ locales: {
451
+ enabled: localesEnabled,
452
+ available: i18n?.locales ?? [],
453
+ default: i18n?.defaultLocale ?? null,
454
+ queryParam: "lang",
455
+ fallbackQueryParam: "locale"
456
+ },
457
+ capabilities: {
458
+ markdownRoutes: true,
459
+ agentMdOverrides: true,
460
+ agentBlocks: true,
461
+ llms: llmsEnabled,
462
+ skills: true,
463
+ mcp: mcp.enabled,
464
+ search: searchEnabled,
465
+ agentFeedback: feedback?.enabled ?? false,
466
+ locales: localesEnabled
467
+ },
468
+ api: {
469
+ docs: DEFAULT_DOCS_API_ROUTE,
470
+ agentSpec: DEFAULT_AGENT_SPEC_ROUTE,
471
+ agentSpecDefault: DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
472
+ agentSpecFallback: DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE,
473
+ agentSpecWellKnown: DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE,
474
+ agentSpecWellKnownJson: DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
475
+ agentSpecQuery: `${DEFAULT_DOCS_API_ROUTE}?agent=spec`
476
+ },
477
+ markdown: {
478
+ enabled: true,
479
+ acceptHeader: markdown?.acceptHeader === false ? null : "text/markdown",
480
+ pagePattern: `/${normalizedEntry}/{slug}.md`,
481
+ rootPage: `/${normalizedEntry}.md`,
482
+ apiPattern: `${DEFAULT_DOCS_API_ROUTE}?format=markdown&path={slug}`,
483
+ resolutionOrder: [
484
+ "agent.md",
485
+ "Agent blocks",
486
+ "page markdown"
487
+ ]
488
+ },
489
+ llms: {
490
+ enabled: llmsEnabled,
491
+ defaultTxt: DEFAULT_LLMS_TXT_ROUTE,
492
+ defaultFull: DEFAULT_LLMS_FULL_TXT_ROUTE,
493
+ txt: `${DEFAULT_DOCS_API_ROUTE}?format=llms`,
494
+ full: `${DEFAULT_DOCS_API_ROUTE}?format=llms-full`,
495
+ publicTxt: DEFAULT_LLMS_TXT_ROUTE,
496
+ publicFull: DEFAULT_LLMS_FULL_TXT_ROUTE,
497
+ wellKnownTxt: DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE,
498
+ wellKnownFull: DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE
499
+ },
500
+ search: {
501
+ enabled: searchEnabled,
502
+ endpoint: `${DEFAULT_DOCS_API_ROUTE}?query={query}`,
503
+ method: "GET",
504
+ queryParam: "query",
505
+ localeParam: "lang"
506
+ },
507
+ skills: {
508
+ enabled: true,
509
+ file: "skill.md",
510
+ route: DEFAULT_SKILL_MD_ROUTE,
511
+ wellKnown: DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE,
512
+ api: `${DEFAULT_DOCS_API_ROUTE}?format=skill`,
513
+ generatedFallback: true,
514
+ registry: "skills.sh",
515
+ install: "npx skills add farming-labs/docs",
516
+ recommended: [{
517
+ name: "getting-started",
518
+ description: "Use for installation, init, framework setup, theme CSS, and first docs.config wiring."
519
+ }]
520
+ },
521
+ mcp: {
522
+ enabled: mcp.enabled,
523
+ endpoint: mcp.route,
524
+ defaultEndpoint: DEFAULT_MCP_PUBLIC_ROUTE,
525
+ publicEndpoint: DEFAULT_MCP_PUBLIC_ROUTE,
526
+ wellKnownEndpoint: DEFAULT_MCP_WELL_KNOWN_ROUTE,
527
+ publicEndpoints: [DEFAULT_MCP_PUBLIC_ROUTE, DEFAULT_MCP_WELL_KNOWN_ROUTE],
528
+ canonicalEndpoint: DEFAULT_MCP_ROUTE,
529
+ name: mcp.name,
530
+ version: mcp.version,
531
+ tools: mcp.tools
532
+ },
533
+ feedback: {
534
+ enabled: feedback?.enabled ?? false,
535
+ schema: feedbackSchemaRoute,
536
+ submit: feedbackRoute,
537
+ schemaQuery: `${DEFAULT_DOCS_API_ROUTE}?feedback=agent&schema=1`,
538
+ submitQuery: `${DEFAULT_DOCS_API_ROUTE}?feedback=agent`
539
+ },
540
+ instructions: {
541
+ preferMarkdownRoutes: true,
542
+ useMcpWhenAvailable: true,
543
+ readFeedbackSchemaBeforeSubmitting: true,
544
+ doNotAssumeFeedbackPayloadShape: true
545
+ }
546
+ };
547
+ }
548
+ function acceptsMarkdown(request) {
549
+ const accept = request.headers.get("accept");
550
+ if (!accept) return false;
551
+ return accept.split(",").map((value) => value.trim()).some((value) => {
552
+ const [mediaType, ...params] = value.split(";").map((part) => part.trim().toLowerCase());
553
+ if (mediaType !== "text/markdown") return false;
554
+ const qualityParam = params.find((param) => param.split("=", 1)[0]?.trim() === "q");
555
+ if (!qualityParam) return true;
556
+ const qualityValue = qualityParam.slice(qualityParam.indexOf("=") + 1).trim();
557
+ const quality = Number.parseFloat(qualityValue);
558
+ return Number.isFinite(quality) ? quality > 0 : true;
559
+ });
560
+ }
561
+ function normalizeRequestedMarkdownPath(entry, requestedPath) {
562
+ const normalizedEntry = `/${normalizeDocsPathSegment(entry) || "docs"}`;
563
+ const trimmed = requestedPath.trim().replace(/\.md$/i, "");
564
+ if (!trimmed) return normalizedEntry;
565
+ const normalized = normalizeDocsUrlPath(trimmed.startsWith("/") ? trimmed : `/${trimmed}`);
566
+ if (normalized === normalizedEntry || normalized.startsWith(`${normalizedEntry}/`)) return normalized;
567
+ const slug = normalizeDocsPathSegment(trimmed);
568
+ return slug ? normalizeDocsUrlPath(`${normalizedEntry}/${slug}`) : normalizedEntry;
569
+ }
570
+ function isSearchEnabled(search) {
571
+ if (search === false) return false;
572
+ if (search && typeof search === "object" && search.enabled === false) return false;
573
+ return true;
574
+ }
575
+ function compactSkillText(value) {
576
+ return value.replace(/\s+/g, " ").trim();
577
+ }
578
+ function truncateSkillDescription(value) {
579
+ const normalized = compactSkillText(value);
580
+ if (normalized.length <= 1024) return normalized;
581
+ return `${normalized.slice(0, 1021).trimEnd()}...`;
582
+ }
583
+ function toYamlString(value) {
584
+ return JSON.stringify(value);
585
+ }
586
+
587
+ //#endregion
588
+ export { buildPageTwitter as A, defineDocs as B, renderDocsMarkdownDocument as C, resolveDocsMarkdownRequest as D, resolveDocsLlmsTxtFormat as E, resolveDocsPath as F, createTheme as I, extendTheme as L, resolveTitle as M, resolveDocsI18n as N, resolveDocsSkillFormat as O, resolveDocsLocale as P, deepMerge as R, 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, resolveOGImage as j, buildPageOpenGraph 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, resolveChangelogConfig as z };
@@ -69,13 +69,27 @@ 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-DHlRFytW.mjs");
72
+ const { init } = await import("../init-9U8AijRu.mjs");
73
73
  await init(initOptions);
74
74
  } else if (parsedCommand.command === "mcp") {
75
- const { runMcp } = await import("../mcp-DLP94P1H.mjs");
75
+ const { runMcp } = await import("../mcp-1j3-_z66.mjs");
76
76
  await runMcp(mcpOptions);
77
+ } else if (parsedCommand.command === "agent" && subcommand === "compact") {
78
+ const { compactAgentDocs, parseAgentCompactArgs, printAgentCompactHelp } = await import("../agent-CoO9z8aW.mjs");
79
+ const agentCompactOptions = parseAgentCompactArgs(args.slice(2));
80
+ if (agentCompactOptions.help) {
81
+ printAgentCompactHelp();
82
+ return;
83
+ }
84
+ await compactAgentDocs(agentCompactOptions);
85
+ } else if (parsedCommand.command === "agent") {
86
+ console.error(pc.red(`Unknown agent subcommand: ${subcommand ?? "(missing)"}`));
87
+ console.error();
88
+ const { printAgentCompactHelp } = await import("../agent-CoO9z8aW.mjs");
89
+ printAgentCompactHelp();
90
+ process.exit(1);
77
91
  } else if (parsedCommand.command === "search" && subcommand === "sync") {
78
- const { syncSearch } = await import("../search-CNesqs89.mjs");
92
+ const { syncSearch } = await import("../search-B2HvbbeE.mjs");
79
93
  await syncSearch(searchSyncOptions);
80
94
  } else if (parsedCommand.command === "search") {
81
95
  console.error(pc.red(`Unknown search subcommand: ${subcommand ?? "(missing)"}`));
@@ -83,7 +97,7 @@ async function main() {
83
97
  printHelp();
84
98
  process.exit(1);
85
99
  } else if (parsedCommand.command === "upgrade") {
86
- const { upgrade } = await import("../upgrade-J_kkv-ti.mjs");
100
+ const { upgrade } = await import("../upgrade-BGWZ_NLh.mjs");
87
101
  await upgrade({
88
102
  framework: (typeof flags.framework === "string" ? flags.framework : void 0) ?? (args[1] && !args[1].startsWith("--") ? args[1] : void 0),
89
103
  tag: args.includes("--beta") ? "beta" : args.includes("--latest") ? "latest" : parsedCommand.tag ?? "latest"
@@ -106,6 +120,7 @@ ${pc.dim("Usage:")}
106
120
 
107
121
  ${pc.dim("Commands:")}
108
122
  ${pc.cyan("init")} Scaffold docs in your project (default)
123
+ ${pc.cyan("agent")} Agent utilities (${pc.dim("compact")} to generate sibling agent.md files)
109
124
  ${pc.cyan("mcp")} Run the built-in docs MCP server over stdio
110
125
  ${pc.cyan("search")} Search utilities (${pc.dim("sync")} for external indexes)
111
126
  ${pc.cyan("upgrade")} Upgrade @farming-labs/* packages to latest (auto-detect or use --framework)
@@ -125,6 +140,16 @@ ${pc.dim("Options for init:")}
125
140
  ${pc.dim("Options for mcp:")}
126
141
  ${pc.cyan("--config <path>")} Use a custom docs config path instead of ${pc.dim("docs.config.ts[x]")}
127
142
 
143
+ ${pc.dim("Options for agent compact:")}
144
+ ${pc.cyan("agent compact <page...>")} Compact pages and write sibling ${pc.dim("agent.md")} files
145
+ ${pc.cyan("agent compact --all")} Compact every folder-based docs page
146
+ ${pc.cyan("--page <slug|path>")} Repeatable explicit page flag; positional page args work too
147
+ ${pc.cyan("--api-key <key>")} Token Company API key (or use ${pc.dim("TOKEN_COMPANY_API_KEY")})
148
+ ${pc.cyan("--api-key-env <name>")} Custom env var name for the Token Company API key
149
+ ${pc.cyan("--base-url <url>")} Override the Token Company API base URL
150
+ ${pc.cyan("--aggressiveness <0-1>")} Compression intensity for compacted output
151
+ ${pc.cyan("--dry-run")} Resolve and compress pages without writing files
152
+
128
153
  ${pc.dim("Options for search sync:")}
129
154
  ${pc.cyan("search sync --typesense")} Sync docs content to Typesense using env/flags
130
155
  ${pc.cyan("search sync --algolia")} Sync docs content to Algolia using env/flags