@farming-labs/docs 0.2.62 → 0.2.64

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.
Files changed (58) hide show
  1. package/dist/agent-CQTH7NFu.mjs +624 -0
  2. package/dist/agent-DKKptIgy.mjs +4365 -0
  3. package/dist/agent-evals-B7MIxuEX.mjs +2144 -0
  4. package/dist/agent-export-CBgWgPvH.mjs +910 -0
  5. package/dist/agent-scope-C_U--OZ7.mjs +283 -0
  6. package/dist/agent-skills-bundle.d.mts +13 -0
  7. package/dist/agent-skills-bundle.mjs +12 -0
  8. package/dist/agent-skills-server-CPja6Syt.d.mts +14 -0
  9. package/dist/agent-skills-server-DraIb6FV.mjs +415 -0
  10. package/dist/agent-skills-vite.d.mts +31 -0
  11. package/dist/agent-skills-vite.mjs +70 -0
  12. package/dist/agents-XWZBub6f.mjs +221 -0
  13. package/dist/analytics-Bx44lg6d.mjs +177 -0
  14. package/dist/cli/index.d.mts +15 -0
  15. package/dist/cli/index.mjs +452 -0
  16. package/dist/client/react.d.mts +45 -0
  17. package/dist/client/react.mjs +223 -0
  18. package/dist/cloud-analytics-CSyFE6SS.mjs +132 -0
  19. package/dist/cloud-ask-ai-sbpjOR2K.mjs +382 -0
  20. package/dist/cloud-ask-ai-zpwkdwnF.d.mts +23 -0
  21. package/dist/cloud-pdNC-tyj.mjs +1615 -0
  22. package/dist/code-blocks-DnNVNK2M.mjs +871 -0
  23. package/dist/codeblocks-CFuurVIH.mjs +250 -0
  24. package/dist/config-Wcdj-D0a.mjs +369 -0
  25. package/dist/dev-Cmy6DtdF.mjs +1333 -0
  26. package/dist/docs-cloud-server.d.mts +70 -0
  27. package/dist/docs-cloud-server.mjs +310 -0
  28. package/dist/doctor-DtGYZ41i.mjs +2036 -0
  29. package/dist/downgrade-w7e6Se0L.mjs +184 -0
  30. package/dist/errors-DbOhkE1h.mjs +20 -0
  31. package/dist/golden-evaluations-Dj-9Eo3v.mjs +1785 -0
  32. package/dist/i18n-CCaFUnAN.mjs +40 -0
  33. package/dist/index.d.mts +1150 -0
  34. package/dist/index.mjs +10 -0
  35. package/dist/init-CQY0Woe3.mjs +1264 -0
  36. package/dist/mcp-B9dcsivk.mjs +156 -0
  37. package/dist/mcp.d.mts +298 -0
  38. package/dist/mcp.mjs +4430 -0
  39. package/dist/metadata-DWExHQnx.mjs +237 -0
  40. package/dist/package-version-n5AFur8a.mjs +128 -0
  41. package/dist/reading-time-CYZ5VvKU.mjs +742 -0
  42. package/dist/review-CLoHTywU.mjs +673 -0
  43. package/dist/robots-BIpC4j4P.mjs +201 -0
  44. package/dist/robots-CUTahhoY.mjs +179 -0
  45. package/dist/search-B6V6qtiI.mjs +1826 -0
  46. package/dist/search-CaSyi6H6.d.mts +279 -0
  47. package/dist/search-DSjCeOk7.mjs +104 -0
  48. package/dist/server.d.mts +343 -0
  49. package/dist/server.mjs +14 -0
  50. package/dist/sitemap-Cykpe3Tz.mjs +249 -0
  51. package/dist/sitemap-server-C_6Wes83.mjs +1137 -0
  52. package/dist/standards-discovery-C4HUqMd2.d.mts +227 -0
  53. package/dist/standards-discovery-jkykaXq1.mjs +519 -0
  54. package/dist/templates-Bq_P7ctv.mjs +2465 -0
  55. package/dist/types-lMBIdZg0.d.mts +3315 -0
  56. package/dist/upgrade-oz-GChgt.mjs +56 -0
  57. package/dist/utils-DpiIioYb.mjs +225 -0
  58. package/package.json +1 -1
@@ -0,0 +1,910 @@
1
+ import { $ as renderDocsAgentsDocument, At as buildDocsSitemapManifest, Ft as resolveDocsSitemapConfig, Nt as renderDocsSitemapMarkdown, Pt as renderDocsSitemapXml, S as DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE, a as DEFAULT_AGENT_MD_ROUTE, at as resolveDocsAgentFeedbackConfig, c as DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE, et as renderDocsLlmsTxt, g as DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE, h as DEFAULT_LLMS_FULL_TXT_ROUTE, k as buildDocsAgentDiscoverySpec, l as DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE, n as DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE, o as DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE, rt as renderDocsSkillDocument, t as DEFAULT_AGENTS_MD_ROUTE, tt as renderDocsMarkdownDocument, v as DEFAULT_LLMS_TXT_ROUTE, vt as toDocsMarkdownUrl, x as DEFAULT_SKILL_MD_ROUTE, y as DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE } from "./agent-DKKptIgy.mjs";
2
+ import { A as resolveDocsPublishedAgentSkill, S as buildDocsAgentSkillsIndex, a as DEFAULT_A2A_AGENT_CARD_ROUTE, c as DEFAULT_AGENT_SKILLS_INDEX_ROUTE, u as DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, v as DEFAULT_LEGACY_SKILLS_INDEX_ROUTE, w as buildDocsLegacySkillsIndex, x as buildDocsA2AAgentCard, y as DEFAULT_LEGACY_SKILLS_ROUTE_PREFIX } from "./standards-discovery-jkykaXq1.mjs";
3
+ import "./search-B6V6qtiI.mjs";
4
+ import { t as resolveDocsI18n } from "./i18n-CCaFUnAN.mjs";
5
+ import { c as renderDocsRobotsGeneratedBlock, f as upsertDocsRobotsGeneratedBlock, i as DOCS_ROBOTS_GENERATED_BLOCK_START, r as DOCS_ROBOTS_GENERATED_BLOCK_END, u as resolveDocsRobotsConfig } from "./robots-BIpC4j4P.mjs";
6
+ import "./sitemap-server-C_6Wes83.mjs";
7
+ import "./agent-evals-B7MIxuEX.mjs";
8
+ import { createFilesystemDocsMcpSource, resolveDocsMcpConfig } from "./mcp.mjs";
9
+ import "./code-blocks-DnNVNK2M.mjs";
10
+ import { t as resolveConfiguredAgentSkills } from "./agent-skills-server-DraIb6FV.mjs";
11
+ import "./server.mjs";
12
+ import { _ as resolveDocsContentDir, a as loadDocsConfigModule, d as readNavTitle, g as resolveDocsConfigPath, h as readTopLevelStringProperty, l as readBooleanProperty, m as readTopLevelBooleanProperty, p as readStringProperty, r as extractTopLevelConfigObject, t as extractNestedObjectLiteral } from "./config-Wcdj-D0a.mjs";
13
+ import { t as detectFramework } from "./utils-DpiIioYb.mjs";
14
+ import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
15
+ import path from "node:path";
16
+ import { execFileSync } from "node:child_process";
17
+ import { createHash, randomUUID } from "node:crypto";
18
+ import pc from "picocolors";
19
+
20
+ //#region src/cli/agent-export.ts
21
+ const AGENT_EXPORT_AGENTS_MARKER = "<!-- Generated by @farming-labs/docs agent export. -->";
22
+ const AGENTS_GENERATE_MARKER = "<!-- Generated by @farming-labs/docs agents generate. -->";
23
+ const AGENT_BUNDLE_MANIFEST_ROUTE = "/.well-known/agent-bundle.json";
24
+ const AGENT_BUNDLE_INTERNAL_MANIFEST = ".farming-labs/agent-bundle-manifest.json";
25
+ const SKILL_CANONICAL_ROUTE = "/skills/docs/SKILL.md";
26
+ function parseInlineFlag(arg) {
27
+ const [rawKey, value] = arg.slice(2).split("=", 2);
28
+ return {
29
+ key: rawKey.trim(),
30
+ value
31
+ };
32
+ }
33
+ function parseAgentExportArgs(argv) {
34
+ const parsed = {};
35
+ for (let index = 0; index < argv.length; index += 1) {
36
+ const arg = argv[index];
37
+ if (arg === "--help" || arg === "-h") {
38
+ parsed.help = true;
39
+ continue;
40
+ }
41
+ if (arg === "--public") {
42
+ parsed.public = true;
43
+ continue;
44
+ }
45
+ if (arg === "--check") {
46
+ parsed.check = true;
47
+ continue;
48
+ }
49
+ if (arg.startsWith("--config=")) {
50
+ const value = parseInlineFlag(arg).value;
51
+ if (!value) throw new Error("Missing value for --config.");
52
+ parsed.configPath = value;
53
+ continue;
54
+ }
55
+ if (arg === "--config") {
56
+ const value = argv[index + 1];
57
+ if (!value || value.startsWith("--")) throw new Error("Missing value for --config.");
58
+ parsed.configPath = value;
59
+ index += 1;
60
+ continue;
61
+ }
62
+ throw new Error(`Unknown agent export flag: ${arg}.`);
63
+ }
64
+ return parsed;
65
+ }
66
+ function normalizePathSegment(value) {
67
+ return value.replace(/^\/+|\/+$/g, "");
68
+ }
69
+ function normalizeContent(content) {
70
+ return `${content.trimEnd()}\n`;
71
+ }
72
+ function encodeRoutePath(relativePath) {
73
+ return relativePath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
74
+ }
75
+ function publicFilePath(publicDir, route) {
76
+ let relativeRoute;
77
+ try {
78
+ relativeRoute = route.replace(/^\/+/, "").split("/").map((segment) => decodeURIComponent(segment)).join("/");
79
+ } catch {
80
+ throw new Error(`Agent Bundle route contains invalid URL encoding: ${route}`);
81
+ }
82
+ const resolved = resolveContainedPath(publicDir, relativeRoute);
83
+ if (!resolved) throw new Error(`Agent Bundle route must stay inside the public directory: ${route}`);
84
+ return resolved;
85
+ }
86
+ function publicRelativePath(publicDir, filePath) {
87
+ return path.relative(publicDir, filePath).replace(/\\/g, "/");
88
+ }
89
+ function resolveContainedPath(baseDir, relativePath) {
90
+ const base = path.resolve(baseDir);
91
+ const resolved = path.resolve(base, relativePath);
92
+ if (!isPathWithin(base, resolved)) return void 0;
93
+ const realBase = resolveThroughNearestExistingAncestor(base);
94
+ const realResolved = resolveThroughNearestExistingAncestor(resolved);
95
+ if (!realBase || !realResolved || !isPathWithin(realBase, realResolved)) return void 0;
96
+ return resolved;
97
+ }
98
+ function isPathWithin(baseDir, candidatePath) {
99
+ const relative = path.relative(baseDir, candidatePath);
100
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
101
+ }
102
+ /**
103
+ * Resolve the existing portion of a path so a symlinked ancestor cannot move a future write or
104
+ * delete outside its declared root. Missing suffixes are appended to the nearest real ancestor.
105
+ */
106
+ function resolveThroughNearestExistingAncestor(absolutePath) {
107
+ let cursor = path.resolve(absolutePath);
108
+ const missingSegments = [];
109
+ while (true) {
110
+ let exists = false;
111
+ try {
112
+ lstatSync(cursor);
113
+ exists = true;
114
+ } catch (error) {
115
+ const code = error.code;
116
+ if (code !== "ENOENT" && code !== "ENOTDIR") return void 0;
117
+ }
118
+ if (exists) try {
119
+ return path.resolve(realpathSync(cursor), ...missingSegments.reverse());
120
+ } catch {
121
+ return;
122
+ }
123
+ const parent = path.dirname(cursor);
124
+ if (parent === cursor) return void 0;
125
+ missingSegments.push(path.basename(cursor));
126
+ cursor = parent;
127
+ }
128
+ }
129
+ function sha256(content) {
130
+ return createHash("sha256").update(content).digest("hex");
131
+ }
132
+ function byteLength(content) {
133
+ return typeof content === "string" ? Buffer.byteLength(content, "utf-8") : content.byteLength;
134
+ }
135
+ function readExisting(filePath) {
136
+ return existsSync(filePath) ? readFileSync(filePath, "utf-8") : void 0;
137
+ }
138
+ function atomicWriteFile(filePath, content) {
139
+ mkdirSync(path.dirname(filePath), { recursive: true });
140
+ const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
141
+ try {
142
+ if (typeof content === "string") writeFileSync(temporaryPath, content, "utf-8");
143
+ else writeFileSync(temporaryPath, content);
144
+ renameSync(temporaryPath, filePath);
145
+ } finally {
146
+ if (existsSync(temporaryPath)) rmSync(temporaryPath, { force: true });
147
+ }
148
+ }
149
+ function readStaticLlmsConfig(content) {
150
+ const enabled = readTopLevelBooleanProperty(content, "llmsTxt");
151
+ if (enabled !== void 0) return enabled;
152
+ const block = extractNestedObjectLiteral(content, ["llmsTxt"]);
153
+ if (!block) return void 0;
154
+ const maxCharsBlock = extractNestedObjectLiteral(content, ["llmsTxt", "maxChars"]);
155
+ return {
156
+ enabled: readBooleanProperty(block, "enabled") ?? true,
157
+ baseUrl: readStringProperty(block, "baseUrl"),
158
+ siteTitle: readStringProperty(block, "siteTitle"),
159
+ siteDescription: readStringProperty(block, "siteDescription"),
160
+ maxChars: maxCharsBlock ? {
161
+ mode: readStringProperty(maxCharsBlock, "mode"),
162
+ chars: readNumericProperty(maxCharsBlock, "chars")
163
+ } : void 0,
164
+ sections: readStaticLlmsSections(block)
165
+ };
166
+ }
167
+ function readNumericProperty(content, key) {
168
+ const match = content.match(new RegExp(`\\b${key}\\b\\s*:\\s*(-?\\d[\\d_]*(?:\\.\\d+)?)`));
169
+ if (!match) return void 0;
170
+ const value = Number(match[1].replace(/_/g, ""));
171
+ return Number.isFinite(value) ? value : void 0;
172
+ }
173
+ function findBalancedDelimiterEnd(content, start, open, close) {
174
+ let depth = 0;
175
+ let quote = null;
176
+ let escaped = false;
177
+ for (let index = start; index < content.length; index += 1) {
178
+ const char = content[index];
179
+ if (quote) {
180
+ if (escaped) escaped = false;
181
+ else if (char === "\\") escaped = true;
182
+ else if (char === quote) quote = null;
183
+ continue;
184
+ }
185
+ if (char === "\"" || char === "'" || char === "`") {
186
+ quote = char;
187
+ continue;
188
+ }
189
+ if (char === open) depth += 1;
190
+ if (char !== close) continue;
191
+ depth -= 1;
192
+ if (depth === 0) return index;
193
+ }
194
+ return -1;
195
+ }
196
+ function extractArrayProperty(content, key) {
197
+ const keyIndex = content.search(new RegExp(`\\b${key}\\b\\s*:\\s*\\[`));
198
+ if (keyIndex === -1) return void 0;
199
+ const start = content.indexOf("[", keyIndex);
200
+ if (start === -1) return void 0;
201
+ const end = findBalancedDelimiterEnd(content, start, "[", "]");
202
+ return end === -1 ? void 0 : content.slice(start + 1, end);
203
+ }
204
+ function readStaticLlmsSections(content) {
205
+ const sections = extractArrayProperty(content, "sections");
206
+ if (!sections) return void 0;
207
+ const result = [];
208
+ for (let index = 0; index < sections.length; index += 1) {
209
+ if (sections[index] !== "{") continue;
210
+ const end = findBalancedDelimiterEnd(sections, index, "{", "}");
211
+ if (end === -1) break;
212
+ const block = sections.slice(index + 1, end);
213
+ const title = readStringProperty(block, "title");
214
+ const match = readStringProperty(block, "match") ?? readStringArrayProperty(block, "match");
215
+ if (title && (typeof match === "string" || match.length > 0)) {
216
+ const maxCharsBlock = extractNestedObjectLiteral(block, ["maxChars"]);
217
+ result.push({
218
+ title,
219
+ description: readStringProperty(block, "description"),
220
+ match,
221
+ maxChars: maxCharsBlock ? {
222
+ mode: readStringProperty(maxCharsBlock, "mode"),
223
+ chars: readNumericProperty(maxCharsBlock, "chars")
224
+ } : void 0
225
+ });
226
+ }
227
+ index = end;
228
+ }
229
+ return result.length > 0 ? result : void 0;
230
+ }
231
+ function readStaticSitemapConfig(content) {
232
+ const enabled = readTopLevelBooleanProperty(content, "sitemap");
233
+ if (enabled !== void 0) return enabled;
234
+ const block = extractNestedObjectLiteral(content, ["sitemap"]);
235
+ if (!block) return void 0;
236
+ const xmlBoolean = readTopLevelBooleanProperty(block, "xml");
237
+ const xmlBlock = extractNestedObjectLiteral(content, ["sitemap", "xml"]);
238
+ const markdownBoolean = readTopLevelBooleanProperty(block, "markdown");
239
+ const markdownBlock = extractNestedObjectLiteral(content, ["sitemap", "markdown"]);
240
+ return {
241
+ enabled: readBooleanProperty(block, "enabled") ?? true,
242
+ routePrefix: readStringProperty(block, "routePrefix"),
243
+ baseUrl: readStringProperty(block, "baseUrl"),
244
+ manifestPath: readStringProperty(block, "manifestPath"),
245
+ xml: xmlBoolean ?? (xmlBlock ? {
246
+ enabled: readBooleanProperty(xmlBlock, "enabled"),
247
+ includeLastmod: readBooleanProperty(xmlBlock, "includeLastmod")
248
+ } : void 0),
249
+ markdown: markdownBoolean ?? (markdownBlock ? {
250
+ enabled: readBooleanProperty(markdownBlock, "enabled"),
251
+ includeDescriptions: readBooleanProperty(markdownBlock, "includeDescriptions"),
252
+ includeLastmod: readBooleanProperty(markdownBlock, "includeLastmod"),
253
+ linkTarget: readStringProperty(markdownBlock, "linkTarget")
254
+ } : void 0)
255
+ };
256
+ }
257
+ function readStaticRobotsConfig(content) {
258
+ const enabled = readTopLevelBooleanProperty(content, "robots");
259
+ if (enabled !== void 0) return enabled;
260
+ const block = extractNestedObjectLiteral(content, ["robots"]);
261
+ if (!block) return void 0;
262
+ const ai = readStringProperty(block, "ai");
263
+ return {
264
+ enabled: readBooleanProperty(block, "enabled") ?? true,
265
+ baseUrl: readStringProperty(block, "baseUrl"),
266
+ ai: ai === "allow" || ai === "disallow" ? ai : readBooleanProperty(block, "ai")
267
+ };
268
+ }
269
+ function readStringArrayProperty(content, key) {
270
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
271
+ const match = content.match(new RegExp(`\\b${escaped}\\b\\s*:\\s*\\[([\\s\\S]*?)\\]`));
272
+ if (!match) return [];
273
+ return [...match[1].matchAll(/["']([^"']+)["']/g)].map((value) => value[1]);
274
+ }
275
+ function readStaticI18nConfig(content) {
276
+ const block = extractNestedObjectLiteral(content, ["i18n"]);
277
+ if (!block) return void 0;
278
+ const locales = readStringArrayProperty(block, "locales");
279
+ if (locales.length === 0) return void 0;
280
+ return {
281
+ locales,
282
+ defaultLocale: readStringProperty(block, "defaultLocale")
283
+ };
284
+ }
285
+ function readStaticMetadataDescription(content) {
286
+ const block = extractNestedObjectLiteral(content, ["metadata"]);
287
+ return block ? readStringProperty(block, "description") : void 0;
288
+ }
289
+ function readStaticDocsPath(content) {
290
+ const configured = readTopLevelStringProperty(content, "docsPath");
291
+ if (configured !== void 0) return configured;
292
+ const root = extractTopLevelConfigObject(content) ?? content;
293
+ return /\bdocsPath\b\s*:\s*(["'])\1/.test(root) ? "" : void 0;
294
+ }
295
+ function buildPageUrl(routeEntry, slug, locale) {
296
+ const parts = [
297
+ routeEntry,
298
+ locale,
299
+ slug
300
+ ].flatMap((value) => value ? [normalizePathSegment(value)] : []);
301
+ return parts.length > 0 ? `/${parts.join("/")}` : "/";
302
+ }
303
+ function staticMarkdownRoute(page, sourceEntry, routeEntry) {
304
+ if (!routeEntry && !page.slug) return `/${sourceEntry || "docs"}.md`;
305
+ return toDocsMarkdownUrl(page.url);
306
+ }
307
+ async function loadPages(options) {
308
+ const localeInputs = options.i18n ? options.i18n.locales.map((locale) => ({
309
+ locale,
310
+ directory: path.join(options.contentDir, locale)
311
+ })) : [{
312
+ locale: void 0,
313
+ directory: options.contentDir
314
+ }];
315
+ const pages = [];
316
+ for (const input of localeInputs) {
317
+ const source = createFilesystemDocsMcpSource({
318
+ rootDir: options.rootDir,
319
+ entry: options.routeEntry || "docs",
320
+ contentDir: input.directory,
321
+ siteTitle: options.siteTitle,
322
+ ordering: options.ordering
323
+ });
324
+ const scanned = await Promise.resolve(source.getPages());
325
+ const localeRoute = input.locale && input.locale !== options.i18n?.defaultLocale ? input.locale : void 0;
326
+ for (const page of scanned) pages.push({
327
+ locale: input.locale,
328
+ page: {
329
+ ...page,
330
+ url: buildPageUrl(options.routeEntry, page.slug, localeRoute)
331
+ }
332
+ });
333
+ }
334
+ return pages;
335
+ }
336
+ function formatDateOnly(value) {
337
+ const parsed = new Date(value);
338
+ if (Number.isNaN(parsed.getTime())) return void 0;
339
+ return parsed.toISOString().slice(0, 10);
340
+ }
341
+ function pageGitLastmod(rootDir, page) {
342
+ if (page.sourcePath) try {
343
+ const lastmod = formatDateOnly(execFileSync("git", [
344
+ "log",
345
+ "-1",
346
+ "--format=%cI",
347
+ "--",
348
+ page.sourcePath
349
+ ], {
350
+ cwd: rootDir,
351
+ encoding: "utf-8",
352
+ stdio: [
353
+ "ignore",
354
+ "pipe",
355
+ "ignore"
356
+ ]
357
+ }).trim());
358
+ if (lastmod) return lastmod;
359
+ } catch {}
360
+ }
361
+ function deterministicSitemapManifest(options) {
362
+ const manifest = buildDocsSitemapManifest({
363
+ pages: options.pages,
364
+ entry: options.entry,
365
+ siteTitle: options.siteTitle,
366
+ baseUrl: options.baseUrl,
367
+ generatedAt: "1970-01-01T00:00:00.000Z",
368
+ resolveLastmod(page) {
369
+ const lastmod = formatDateOnly(page.lastModified ?? "");
370
+ return lastmod ? {
371
+ lastmod,
372
+ lastmodSource: "git"
373
+ } : void 0;
374
+ }
375
+ });
376
+ manifest.generatedAt = `${manifest.pages.reduce((current, page) => {
377
+ if (!page.lastmod) return current;
378
+ return !current || page.lastmod > current ? page.lastmod : current;
379
+ }, void 0) ?? "1970-01-01"}T00:00:00.000Z`;
380
+ return manifest;
381
+ }
382
+ function isManagedAgentsContent(content) {
383
+ return content.includes(AGENT_EXPORT_AGENTS_MARKER) || content.includes(AGENTS_GENERATE_MARKER);
384
+ }
385
+ function findPublicAgentsSource(publicDir) {
386
+ for (const route of [
387
+ DEFAULT_AGENTS_MD_ROUTE,
388
+ DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE,
389
+ DEFAULT_AGENT_MD_ROUTE,
390
+ DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE
391
+ ]) {
392
+ const content = readExisting(publicFilePath(publicDir, route));
393
+ if (content !== void 0 && !isManagedAgentsContent(content)) return normalizeContent(content);
394
+ }
395
+ }
396
+ function addOutput(outputs, publicDir, route, kind, mediaType, content, managed = true, normalizeText = true) {
397
+ const absolutePath = publicFilePath(publicDir, route);
398
+ if (absolutePath === publicFilePath(publicDir, AGENT_BUNDLE_MANIFEST_ROUTE)) throw new Error(`Agent Bundle output collision at ${route} (reserved bundle manifest). Change the conflicting docs slug or configured route.`);
399
+ const collision = outputs.find((output) => output.absolutePath === absolutePath);
400
+ if (collision) throw new Error(`Agent Bundle output collision at ${route} (${collision.kind} and ${kind}). Change the conflicting docs slug or configured route.`);
401
+ outputs.push({
402
+ absolutePath,
403
+ publicPath: publicRelativePath(publicDir, absolutePath),
404
+ route,
405
+ kind,
406
+ mediaType,
407
+ content: typeof content === "string" && normalizeText ? normalizeContent(content) : content,
408
+ managed
409
+ });
410
+ }
411
+ function assertOutputPathsDoNotCollide(options) {
412
+ const claimed = new Map([[options.publicManifestPath, "public Agent Bundle manifest"], [options.internalManifestPath, "internal Agent Bundle manifest"]]);
413
+ for (const output of options.outputs) {
414
+ const owner = claimed.get(output.absolutePath);
415
+ if (owner) throw new Error(`Agent Bundle output collision at ${output.route} (${owner} and ${output.kind}). Change the conflicting docs slug or configured route.`);
416
+ claimed.set(output.absolutePath, `${output.kind} output`);
417
+ }
418
+ for (const output of options.internalOutputs) {
419
+ const owner = claimed.get(output.absolutePath);
420
+ if (owner) throw new Error(`Agent Bundle output collision at ${output.path} (${owner} and internal ${output.kind}). Change the conflicting manifest path.`);
421
+ claimed.set(output.absolutePath, `internal ${output.kind} output`);
422
+ }
423
+ }
424
+ function resolveUserOwnedAgentsOutputs(outputs) {
425
+ return outputs.map((output) => {
426
+ if (output.kind !== "agents") return output;
427
+ const current = readExisting(output.absolutePath);
428
+ if (current === void 0 || isManagedAgentsContent(current)) return output;
429
+ return {
430
+ ...output,
431
+ content: normalizeContent(current),
432
+ managed: false
433
+ };
434
+ });
435
+ }
436
+ function resolveUserOwnedOverrides(outputs, previous) {
437
+ const preserveKinds = new Set([
438
+ "page",
439
+ "llms",
440
+ "discovery",
441
+ "skill"
442
+ ]);
443
+ const previousByPath = new Map(previous?.files.map((file) => [file.path, file]) ?? []);
444
+ return outputs.map((output) => {
445
+ if (!preserveKinds.has(output.kind)) return output;
446
+ if (output.route.startsWith("/.well-known/agent-skills/") || output.route.startsWith("/.well-known/skills/") || output.route === DEFAULT_A2A_AGENT_CARD_ROUTE) return output;
447
+ if (typeof output.content !== "string") return output;
448
+ const current = readExisting(output.absolutePath);
449
+ if (current === void 0 || current === output.content) return output;
450
+ const previousFile = previousByPath.get(output.publicPath);
451
+ if (previousFile?.managed && sha256(current) === previousFile.sha256) return output;
452
+ return {
453
+ ...output,
454
+ content: normalizeContent(current),
455
+ managed: false
456
+ };
457
+ });
458
+ }
459
+ function resolveRobotsOutput(publicDir, entry, sitemap, robots, agentCard = false) {
460
+ const route = "/robots.txt";
461
+ const absolutePath = publicFilePath(publicDir, route);
462
+ const existing = readExisting(absolutePath);
463
+ const generatedBlock = renderDocsRobotsGeneratedBlock({
464
+ entry,
465
+ apiCatalog: false,
466
+ sitemap,
467
+ baseUrl: robots.baseUrl,
468
+ robots,
469
+ agentCard
470
+ });
471
+ const hasManagedBlock = existing?.includes(DOCS_ROBOTS_GENERATED_BLOCK_START) === true && existing.includes(DOCS_ROBOTS_GENERATED_BLOCK_END);
472
+ if (existing !== void 0 && !hasManagedBlock) return {
473
+ absolutePath,
474
+ publicPath: publicRelativePath(publicDir, absolutePath),
475
+ route,
476
+ kind: "robots",
477
+ mediaType: "text/plain",
478
+ content: normalizeContent(existing),
479
+ managed: false
480
+ };
481
+ return {
482
+ absolutePath,
483
+ publicPath: publicRelativePath(publicDir, absolutePath),
484
+ route,
485
+ kind: "robots",
486
+ mediaType: "text/plain",
487
+ content: normalizeContent(existing && hasManagedBlock ? upsertDocsRobotsGeneratedBlock(existing, generatedBlock) : generatedBlock),
488
+ managed: true
489
+ };
490
+ }
491
+ function toManifestFile(output) {
492
+ return {
493
+ path: output.publicPath,
494
+ route: output.route,
495
+ kind: output.kind,
496
+ mediaType: output.mediaType,
497
+ bytes: byteLength(output.content),
498
+ sha256: sha256(output.content),
499
+ managed: output.managed
500
+ };
501
+ }
502
+ function buildAgentBundleManifest(options) {
503
+ const files = [...options.outputs.map(toManifestFile), ...options.orphanedFiles].sort((left, right) => left.path.localeCompare(right.path));
504
+ const internalFiles = [...options.internalOutputs.map((output) => ({
505
+ path: output.path,
506
+ kind: output.kind,
507
+ bytes: byteLength(output.content),
508
+ sha256: sha256(output.content)
509
+ })), ...options.orphanedInternalFiles].sort((left, right) => left.path.localeCompare(right.path));
510
+ const contentHash = sha256([...files.map((file) => `public:${file.path}\0${file.sha256}`), ...internalFiles.map((file) => `internal:${file.path}\0${file.sha256}`)].join("\n"));
511
+ return {
512
+ format: "farming-labs-agent-bundle.v1",
513
+ generator: "@farming-labs/docs",
514
+ entry: options.entry,
515
+ publicDirectory: options.publicDirectory,
516
+ baseUrl: options.baseUrl,
517
+ contentHash,
518
+ files,
519
+ internalFiles,
520
+ pages: options.pages.map(({ page, locale }) => ({
521
+ route: page.url,
522
+ markdownRoute: staticMarkdownRoute(page, options.sourceEntry, options.entry),
523
+ sourcePath: page.sourcePath,
524
+ locale
525
+ })).sort((left, right) => left.markdownRoute.localeCompare(right.markdownRoute))
526
+ };
527
+ }
528
+ function readPreviousManifest(manifestPath) {
529
+ if (!existsSync(manifestPath)) return void 0;
530
+ try {
531
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
532
+ if (manifest.format !== "farming-labs-agent-bundle.v1" || !Array.isArray(manifest.files) || !Array.isArray(manifest.pages)) return;
533
+ return {
534
+ ...manifest,
535
+ files: manifest.files.filter((file) => file && typeof file.path === "string" && typeof file.sha256 === "string" && typeof file.managed === "boolean"),
536
+ internalFiles: Array.isArray(manifest.internalFiles) ? manifest.internalFiles.filter((file) => file && typeof file.path === "string" && typeof file.sha256 === "string") : []
537
+ };
538
+ } catch {
539
+ return;
540
+ }
541
+ }
542
+ function staleManagedPaths(publicDir, previous, outputs) {
543
+ if (!previous) return [];
544
+ const expected = new Set(outputs.map((output) => output.publicPath));
545
+ return previous.files.filter((file) => (file.managed || file.orphaned === true) && !expected.has(file.path)).map((file) => resolvePreviousOutputPath(publicDir, file.path, "public")).filter((filePath) => existsSync(filePath));
546
+ }
547
+ function staleInternalPaths(rootDir, previous, outputs) {
548
+ if (!previous?.internalFiles) return [];
549
+ const expected = new Set(outputs.map((output) => output.path));
550
+ return previous.internalFiles.filter((file) => !expected.has(file.path)).map((file) => resolvePreviousOutputPath(rootDir, file.path, "internal")).filter((filePath) => existsSync(filePath));
551
+ }
552
+ function resolvePreviousOutputPath(baseDir, relativePath, kind) {
553
+ const resolved = resolveContainedPath(baseDir, relativePath);
554
+ if (!resolved) throw new Error(`Previous Agent Bundle manifest contains an unsafe ${kind} output path: ${relativePath}`);
555
+ return resolved;
556
+ }
557
+ function preservedStaleManifestFiles(publicDir, previous, outputs) {
558
+ if (!previous) return [];
559
+ const expected = new Set(outputs.map((output) => output.publicPath));
560
+ return previous.files.flatMap((file) => {
561
+ if (!file.managed && file.orphaned !== true || expected.has(file.path)) return [];
562
+ const filePath = resolvePreviousOutputPath(publicDir, file.path, "public");
563
+ if (!existsSync(filePath)) return [];
564
+ const current = readFileSync(filePath);
565
+ if (file.orphaned !== true && sha256(current) === file.sha256) return [];
566
+ return [{
567
+ ...file,
568
+ bytes: byteLength(current),
569
+ sha256: sha256(current),
570
+ managed: false,
571
+ orphaned: true
572
+ }];
573
+ });
574
+ }
575
+ function preservedStaleInternalManifestFiles(rootDir, previous, outputs) {
576
+ if (!previous?.internalFiles) return [];
577
+ const expected = new Set(outputs.map((output) => output.path));
578
+ return previous.internalFiles.flatMap((file) => {
579
+ if (expected.has(file.path)) return [];
580
+ const filePath = resolvePreviousOutputPath(rootDir, file.path, "internal");
581
+ const current = existsSync(filePath) ? readFileSync(filePath) : void 0;
582
+ if (current === void 0) return [];
583
+ if (file.orphaned !== true && sha256(current) === file.sha256) return [];
584
+ return [{
585
+ ...file,
586
+ bytes: byteLength(current),
587
+ sha256: sha256(current),
588
+ orphaned: true
589
+ }];
590
+ });
591
+ }
592
+ function removeStaleManagedFiles(publicDir, previous, stalePaths) {
593
+ if (!previous) return [];
594
+ const removed = [];
595
+ const previousByPath = new Map(previous.files.map((file) => [resolvePreviousOutputPath(publicDir, file.path, "public"), file]));
596
+ for (const filePath of stalePaths) {
597
+ const previousFile = previousByPath.get(filePath);
598
+ const current = existsSync(filePath) ? readFileSync(filePath) : void 0;
599
+ if (!previousFile?.managed || previousFile.orphaned === true || current === void 0 || sha256(current) !== previousFile.sha256) continue;
600
+ rmSync(filePath);
601
+ removed.push(filePath);
602
+ }
603
+ return removed;
604
+ }
605
+ function removeStaleInternalFiles(rootDir, previous, stalePaths) {
606
+ if (!previous?.internalFiles) return [];
607
+ const previousByPath = new Map(previous.internalFiles.map((file) => [resolvePreviousOutputPath(rootDir, file.path, "internal"), file]));
608
+ const removed = [];
609
+ for (const filePath of stalePaths) {
610
+ const previousFile = previousByPath.get(filePath);
611
+ const current = readExisting(filePath);
612
+ if (!previousFile || previousFile.orphaned === true || current === void 0 || sha256(current) !== previousFile.sha256) continue;
613
+ rmSync(filePath);
614
+ removed.push(filePath);
615
+ }
616
+ return removed;
617
+ }
618
+ function changedOutputs(outputs) {
619
+ return outputs.filter((output) => !outputMatchesExisting(output));
620
+ }
621
+ function outputMatchesExisting(output) {
622
+ if (!existsSync(output.absolutePath)) return false;
623
+ if (typeof output.content === "string") return readExisting(output.absolutePath) === output.content;
624
+ const current = readFileSync(output.absolutePath);
625
+ return current.byteLength === output.content.byteLength && current.equals(Buffer.from(output.content));
626
+ }
627
+ async function exportAgentBundle(options = {}) {
628
+ if (!options.public && !options.check) throw new Error("Pass --public to write the static Agent Bundle into the framework public directory.");
629
+ const rootDir = process.cwd();
630
+ const loadedConfigModule = await loadDocsConfigModule(rootDir, options.configPath);
631
+ const configContent = readFileSync(loadedConfigModule?.path ?? resolveDocsConfigPath(rootDir, options.configPath), "utf-8");
632
+ const config = loadedConfigModule?.config;
633
+ const sourceEntry = normalizePathSegment(config?.entry ?? readTopLevelStringProperty(configContent, "entry") ?? "docs");
634
+ const routeEntry = normalizePathSegment(config?.docsPath ?? readStaticDocsPath(configContent) ?? sourceEntry);
635
+ const contentDir = config?.contentDir ?? resolveDocsContentDir(rootDir, configContent, sourceEntry || "docs");
636
+ const siteTitle = typeof config?.nav?.title === "string" ? config.nav.title : readNavTitle(configContent) ?? "Documentation";
637
+ const metadataDescription = config?.metadata?.description ?? readStaticMetadataDescription(configContent);
638
+ const llmsInput = config?.llmsTxt ?? readStaticLlmsConfig(configContent) ?? true;
639
+ const llmsConfig = typeof llmsInput === "object" ? llmsInput : void 0;
640
+ const llmsEnabled = llmsInput !== false && !(typeof llmsInput === "object" && llmsInput.enabled === false);
641
+ const siteDescription = llmsConfig?.siteDescription ?? metadataDescription;
642
+ const resolvedSiteTitle = llmsConfig?.siteTitle ?? siteTitle;
643
+ const sitemapInput = config?.sitemap ?? readStaticSitemapConfig(configContent) ?? true;
644
+ const robotsInput = config?.robots ?? readStaticRobotsConfig(configContent) ?? true;
645
+ const baseUrl = llmsConfig?.baseUrl ?? (typeof sitemapInput === "object" ? sitemapInput.baseUrl : void 0) ?? (typeof robotsInput === "object" ? robotsInput.baseUrl : void 0);
646
+ const i18n = resolveDocsI18n(config?.i18n ?? readStaticI18nConfig(configContent));
647
+ const publicDirectory = detectFramework(rootDir) === "sveltekit" ? "static" : "public";
648
+ const publicDir = resolveContainedPath(rootDir, publicDirectory);
649
+ if (!publicDir) throw new Error(`Agent Bundle public directory must resolve inside the project root: ${publicDirectory}`);
650
+ const internalManifestPath = resolveContainedPath(rootDir, AGENT_BUNDLE_INTERNAL_MANIFEST);
651
+ if (!internalManifestPath) throw new Error(`Agent Bundle internal manifest must resolve inside the project root: ${AGENT_BUNDLE_INTERNAL_MANIFEST}`);
652
+ const publicManifestPath = publicFilePath(publicDir, AGENT_BUNDLE_MANIFEST_ROUTE);
653
+ const previous = readPreviousManifest(internalManifestPath);
654
+ const localizedPages = (await loadPages({
655
+ rootDir,
656
+ contentDir,
657
+ routeEntry,
658
+ siteTitle: resolvedSiteTitle,
659
+ ordering: config?.ordering,
660
+ i18n
661
+ })).map(({ page, locale }) => {
662
+ const gitLastmod = pageGitLastmod(rootDir, page);
663
+ return {
664
+ locale,
665
+ page: {
666
+ ...page,
667
+ lastModified: gitLastmod
668
+ }
669
+ };
670
+ });
671
+ if (localizedPages.length === 0) throw new Error(`No docs content was found under ${contentDir}.`);
672
+ const pages = localizedPages.map(({ page }) => page);
673
+ const sitemap = resolveDocsSitemapConfig(sitemapInput, { baseUrl });
674
+ const robots = resolveDocsRobotsConfig(robotsInput, { baseUrl: typeof robotsInput === "object" ? robotsInput.baseUrl ?? sitemap.baseUrl : sitemap.baseUrl });
675
+ const mcp = resolveDocsMcpConfig(false, { defaultName: resolvedSiteTitle });
676
+ const feedbackConfig = resolveDocsAgentFeedbackConfig(false);
677
+ const feedback = {
678
+ enabled: feedbackConfig.enabled,
679
+ route: feedbackConfig.route,
680
+ schemaRoute: feedbackConfig.schemaRoute
681
+ };
682
+ const openapi = { enabled: false };
683
+ const llms = {
684
+ enabled: llmsEnabled,
685
+ apiCatalog: false,
686
+ baseUrl,
687
+ siteTitle: resolvedSiteTitle,
688
+ siteDescription,
689
+ maxChars: llmsConfig?.maxChars,
690
+ sections: llmsConfig?.sections,
691
+ openapi
692
+ };
693
+ const documentOptions = {
694
+ origin: baseUrl ?? "/",
695
+ entry: routeEntry || "docs",
696
+ apiRoute: config?.cloud?.apiRoute,
697
+ apiCatalog: false,
698
+ search: false,
699
+ mcp,
700
+ feedback,
701
+ llms,
702
+ sitemap: sitemapInput,
703
+ robots: robotsInput,
704
+ openapi,
705
+ markdown: { acceptHeader: false }
706
+ };
707
+ const configuredAgentSkills = await resolveConfiguredAgentSkills(config?.agent?.skills, { rootDir });
708
+ let outputs = [];
709
+ const internalOutputs = [];
710
+ for (const { page } of localizedPages) addOutput(outputs, publicDir, staticMarkdownRoute(page, sourceEntry, routeEntry), "page", "text/markdown", renderDocsMarkdownDocument({
711
+ ...page,
712
+ markdownUrl: staticMarkdownRoute(page, sourceEntry, routeEntry)
713
+ }, {
714
+ llms,
715
+ origin: baseUrl,
716
+ sitemap: sitemapInput
717
+ }));
718
+ if (llmsEnabled) {
719
+ const llmsContent = renderDocsLlmsTxt(pages.map((page) => ({
720
+ url: page.url,
721
+ markdownUrl: staticMarkdownRoute(page, sourceEntry, routeEntry),
722
+ title: page.title,
723
+ description: page.description,
724
+ content: page.content,
725
+ rawContent: page.rawContent,
726
+ agentContent: page.agentContent,
727
+ agentRawContent: page.agentRawContent,
728
+ agentFallbackContent: page.agentFallbackContent,
729
+ agentFallbackRawContent: page.agentFallbackRawContent
730
+ })), llms);
731
+ for (const route of [DEFAULT_LLMS_TXT_ROUTE, DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE]) addOutput(outputs, publicDir, route, "llms", "text/plain", llmsContent.llmsTxt);
732
+ for (const route of [DEFAULT_LLMS_FULL_TXT_ROUTE, DEFAULT_LLMS_FULL_TXT_WELL_KNOWN_ROUTE]) addOutput(outputs, publicDir, route, "llms", "text/plain", llmsContent.llmsFullTxt);
733
+ const sectionRoutes = new Set(llmsContent.sections.flatMap((section) => [section.route, section.fullRoute]));
734
+ const llmsBaseRoutes = new Set([sourceEntry, routeEntry].filter(Boolean));
735
+ for (const llmsBaseRoute of llmsBaseRoutes) {
736
+ const entryLlmsRoute = `/${llmsBaseRoute}/llms.txt`;
737
+ const entryLlmsFullRoute = `/${llmsBaseRoute}/llms-full.txt`;
738
+ if (!sectionRoutes.has(entryLlmsRoute)) addOutput(outputs, publicDir, entryLlmsRoute, "llms", "text/plain", llmsContent.llmsTxt);
739
+ if (!sectionRoutes.has(entryLlmsFullRoute)) addOutput(outputs, publicDir, entryLlmsFullRoute, "llms", "text/plain", llmsContent.llmsFullTxt);
740
+ }
741
+ for (const section of llmsContent.sections) {
742
+ addOutput(outputs, publicDir, section.route, "llms", "text/plain", section.llmsTxt);
743
+ addOutput(outputs, publicDir, section.fullRoute, "llms", "text/plain", section.llmsFullTxt);
744
+ }
745
+ }
746
+ const generatedSkill = renderDocsSkillDocument(documentOptions);
747
+ const rootSkillPath = path.join(rootDir, "skill.md");
748
+ const publicSkill = existsSync(rootSkillPath) ? readFileSync(rootSkillPath, "utf-8") : generatedSkill;
749
+ for (const route of [DEFAULT_SKILL_MD_ROUTE, DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE]) addOutput(outputs, publicDir, route, "skill", "text/markdown", publicSkill);
750
+ addOutput(outputs, publicDir, SKILL_CANONICAL_ROUTE, "skill", "text/markdown", generatedSkill);
751
+ const generatedAgents = `${AGENT_EXPORT_AGENTS_MARKER}\n${renderDocsAgentsDocument(documentOptions)}`;
752
+ const publicAgentsSource = findPublicAgentsSource(publicDir) ?? generatedAgents;
753
+ for (const route of [
754
+ DEFAULT_AGENTS_MD_ROUTE,
755
+ DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE,
756
+ DEFAULT_AGENT_MD_ROUTE,
757
+ DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE
758
+ ]) addOutput(outputs, publicDir, route, "agents", "text/markdown", publicAgentsSource);
759
+ outputs = resolveUserOwnedAgentsOutputs(outputs);
760
+ if (sitemap.enabled) {
761
+ const sitemapManifest = deterministicSitemapManifest({
762
+ pages: pages.map((page) => ({
763
+ ...page,
764
+ markdownUrl: staticMarkdownRoute(page, sourceEntry, routeEntry)
765
+ })),
766
+ entry: routeEntry || "docs",
767
+ siteTitle: resolvedSiteTitle,
768
+ baseUrl: sitemap.baseUrl
769
+ });
770
+ const sitemapManifestPath = resolveContainedPath(rootDir, sitemap.manifestPath);
771
+ if (!sitemapManifestPath) throw new Error("sitemap.manifestPath must stay inside the project root for agent export.");
772
+ if (sitemapManifestPath === internalManifestPath) throw new Error(`sitemap.manifestPath conflicts with ${AGENT_BUNDLE_INTERNAL_MANIFEST}. Choose a different sitemap manifest path.`);
773
+ internalOutputs.push({
774
+ absolutePath: sitemapManifestPath,
775
+ path: path.relative(rootDir, sitemapManifestPath).replace(/\\/g, "/"),
776
+ kind: "sitemap",
777
+ content: `${JSON.stringify(sitemapManifest, null, 2)}\n`
778
+ });
779
+ if (sitemap.xml.enabled) addOutput(outputs, publicDir, sitemap.xml.route, "sitemap", "application/xml", renderDocsSitemapXml(sitemapManifest, {
780
+ baseUrl: sitemap.baseUrl,
781
+ includeLastmod: sitemap.xml.includeLastmod
782
+ }));
783
+ if (sitemap.markdown.enabled) {
784
+ const sitemapMarkdown = renderDocsSitemapMarkdown(sitemapManifest, {
785
+ baseUrl: sitemap.baseUrl,
786
+ includeDescriptions: sitemap.markdown.includeDescriptions,
787
+ includeLastmod: sitemap.markdown.includeLastmod,
788
+ linkTarget: sitemap.markdown.linkTarget
789
+ });
790
+ for (const route of [
791
+ sitemap.markdown.route,
792
+ sitemap.markdown.docsRoute,
793
+ sitemap.markdown.wellKnownRoute
794
+ ]) if (route) addOutput(outputs, publicDir, route, "sitemap", "text/markdown", sitemapMarkdown);
795
+ }
796
+ }
797
+ if (robots.enabled) outputs.push(resolveRobotsOutput(publicDir, routeEntry || "docs", sitemapInput, robots, Boolean(config?.agent?.a2a)));
798
+ outputs = resolveUserOwnedOverrides(outputs, previous);
799
+ const effectiveSkillOutput = outputs.find((output) => output.route === DEFAULT_SKILL_MD_ROUTE)?.content;
800
+ const allPublishedSkills = [await resolveDocsPublishedAgentSkill({
801
+ preferredDocument: typeof effectiveSkillOutput === "string" ? effectiveSkillOutput : normalizeContent(publicSkill),
802
+ fallbackDocument: normalizeContent(generatedSkill)
803
+ }), ...configuredAgentSkills];
804
+ const publishedNames = /* @__PURE__ */ new Set();
805
+ for (const skill of allPublishedSkills) {
806
+ if (publishedNames.has(skill.name)) throw new Error(`Duplicate published Agent Skill name: ${skill.name}`);
807
+ publishedNames.add(skill.name);
808
+ addOutput(outputs, publicDir, skill.url, "skill", skill.type === "archive" ? "application/gzip" : "text/markdown", skill.content, true, false);
809
+ for (const file of skill.files) {
810
+ if (file.url !== skill.url) addOutput(outputs, publicDir, file.url, "skill", file.mediaType, file.content, true, false);
811
+ if (file.path === "SKILL.md") addOutput(outputs, publicDir, `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/${encodeURIComponent(skill.name)}/skill.md`, "skill", file.mediaType, file.content, true, false);
812
+ const legacyPath = file.path === "SKILL.md" ? "skill.md" : encodeRoutePath(file.path);
813
+ addOutput(outputs, publicDir, `${DEFAULT_LEGACY_SKILLS_ROUTE_PREFIX}/${encodeURIComponent(skill.name)}/${legacyPath}`, "skill", file.mediaType, file.content, true, false);
814
+ }
815
+ }
816
+ addOutput(outputs, publicDir, DEFAULT_AGENT_SKILLS_INDEX_ROUTE, "discovery", "application/json", JSON.stringify(buildDocsAgentSkillsIndex(allPublishedSkills), null, 2));
817
+ addOutput(outputs, publicDir, DEFAULT_LEGACY_SKILLS_INDEX_ROUTE, "discovery", "application/json", JSON.stringify(buildDocsLegacySkillsIndex(allPublishedSkills), null, 2));
818
+ if (config?.agent?.a2a) addOutput(outputs, publicDir, DEFAULT_A2A_AGENT_CARD_ROUTE, "discovery", "application/json", JSON.stringify(buildDocsA2AAgentCard(config.agent.a2a, allPublishedSkills), null, 2));
819
+ const discoveryWithBundle = {
820
+ ...buildDocsAgentDiscoverySpec({
821
+ ...documentOptions,
822
+ i18n,
823
+ publishedSkills: allPublishedSkills,
824
+ agentCard: config?.agent?.a2a
825
+ }),
826
+ staticBundle: {
827
+ format: "farming-labs-agent-bundle.v1",
828
+ manifest: AGENT_BUNDLE_MANIFEST_ROUTE,
829
+ check: "docs agent export --check",
830
+ locales: i18n ? {
831
+ default: i18n.defaultLocale,
832
+ pathPattern: `${routeEntry ? `/${routeEntry}` : ""}/{locale}/{slug}.md`
833
+ } : void 0
834
+ }
835
+ };
836
+ const discoveryJson = JSON.stringify(discoveryWithBundle, null, 2);
837
+ for (const route of [DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE, DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE]) addOutput(outputs, publicDir, route, "discovery", "application/json", discoveryJson);
838
+ outputs = resolveUserOwnedOverrides(outputs, previous);
839
+ assertOutputPathsDoNotCollide({
840
+ outputs,
841
+ internalOutputs,
842
+ publicManifestPath,
843
+ internalManifestPath
844
+ });
845
+ outputs.sort((left, right) => left.publicPath.localeCompare(right.publicPath));
846
+ const stalePaths = staleManagedPaths(publicDir, previous, outputs);
847
+ const staleInternal = staleInternalPaths(rootDir, previous, internalOutputs);
848
+ const orphanedFiles = preservedStaleManifestFiles(publicDir, previous, outputs);
849
+ const orphanedInternalFiles = preservedStaleInternalManifestFiles(rootDir, previous, internalOutputs);
850
+ const manifest = buildAgentBundleManifest({
851
+ sourceEntry,
852
+ entry: routeEntry,
853
+ publicDirectory,
854
+ baseUrl,
855
+ outputs,
856
+ internalOutputs,
857
+ orphanedFiles,
858
+ orphanedInternalFiles,
859
+ pages: localizedPages
860
+ });
861
+ const manifestJson = `${JSON.stringify(manifest, null, 2)}\n`;
862
+ const changed = changedOutputs(outputs);
863
+ const changedInternalOutputs = internalOutputs.filter((output) => readExisting(output.absolutePath) !== output.content);
864
+ if (readExisting(publicManifestPath) !== manifestJson) changed.push({
865
+ absolutePath: publicManifestPath,
866
+ publicPath: publicRelativePath(publicDir, publicManifestPath),
867
+ route: AGENT_BUNDLE_MANIFEST_ROUTE,
868
+ kind: "discovery",
869
+ mediaType: "application/json",
870
+ content: manifestJson,
871
+ managed: true
872
+ });
873
+ const internalChanged = readExisting(internalManifestPath) !== manifestJson;
874
+ if (options.check && (changed.length > 0 || changedInternalOutputs.length > 0 || internalChanged || stalePaths.length > 0 || staleInternal.length > 0)) throw new Error(`Static Agent Bundle is stale (${changed.length + changedInternalOutputs.length + (internalChanged ? 1 : 0) + stalePaths.length + staleInternal.length} output(s)). Run \`docs agent export --public\` to update it.`);
875
+ if (!options.check) {
876
+ for (const output of outputs) if (!outputMatchesExisting(output)) atomicWriteFile(output.absolutePath, output.content);
877
+ for (const output of internalOutputs) if (readExisting(output.absolutePath) !== output.content) atomicWriteFile(output.absolutePath, output.content);
878
+ atomicWriteFile(publicManifestPath, manifestJson);
879
+ atomicWriteFile(internalManifestPath, manifestJson);
880
+ const removed = removeStaleManagedFiles(publicDir, previous, stalePaths);
881
+ removed.push(...removeStaleInternalFiles(rootDir, previous, staleInternal));
882
+ for (const filePath of removed) console.log(pc.dim(`Removed ${path.relative(rootDir, filePath).replace(/\\/g, "/")}`));
883
+ }
884
+ const managedCount = manifest.files.filter((file) => file.managed).length;
885
+ const customCount = manifest.files.length - managedCount;
886
+ const orphanedCount = manifest.files.filter((file) => file.orphaned === true).length + manifest.internalFiles.filter((file) => file.orphaned === true).length;
887
+ console.log(pc.green(options.check ? `Static Agent Bundle is current (${manifest.files.length} files, ${manifest.pages.length} pages).` : `Exported Static Agent Bundle (${manifest.files.length} files, ${manifest.pages.length} pages).`));
888
+ console.log(pc.dim(`${publicDirectory}/.well-known/agent-bundle.json`));
889
+ console.log(pc.dim(AGENT_BUNDLE_INTERNAL_MANIFEST));
890
+ if (customCount > 0) console.log(pc.yellow(`Preserved ${customCount} user-owned public file(s).`));
891
+ if (orphanedCount > 0) console.log(pc.yellow(`Preserved ${orphanedCount} modified obsolete output(s); remove them manually, then rerun the export.`));
892
+ }
893
+ function printAgentExportHelp() {
894
+ console.log(`
895
+ ${pc.bold("docs agent export")} — Export a complete static Agent Bundle.
896
+
897
+ ${pc.dim("Usage:")}
898
+ pnpm exec docs ${pc.cyan("agent export --public")}
899
+ pnpm exec docs ${pc.cyan("agent export --check")}
900
+
901
+ ${pc.dim("Options:")}
902
+ ${pc.cyan("--public")} Write static agent files to ${pc.dim("public/")} or SvelteKit ${pc.dim("static/")}
903
+ ${pc.cyan("--check")} Fail when exported files or their SHA-256 manifest are stale
904
+ ${pc.cyan("--config <path>")} Use a custom docs config path instead of ${pc.dim("docs.config.ts[x]")}
905
+ ${pc.cyan("-h, --help")} Show this help message
906
+ `);
907
+ }
908
+
909
+ //#endregion
910
+ export { exportAgentBundle, parseAgentExportArgs, printAgentExportHelp };