@farming-labs/docs 0.2.52 → 0.2.53

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