@farming-labs/docs 0.2.52 → 0.2.54

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 (51) hide show
  1. package/dist/agent-BXvi0uhS.mjs +623 -0
  2. package/dist/{agent-CaOBIVFy.mjs → agent-DXkXi-pS.mjs} +765 -22
  3. package/dist/agent-evals-BD17jOqL.mjs +1166 -0
  4. package/dist/agent-export-n-a0GbeU.mjs +854 -0
  5. package/dist/agent-scope-B8lptqCd.mjs +283 -0
  6. package/dist/agent-surface-drift-LS8zQgbq.mjs +1429 -0
  7. package/dist/{agents-DjhjZaNP.mjs → agents-BJaRQimo.mjs} +7 -5
  8. package/dist/{analytics-BZEwTK-8.mjs → analytics-Bx44lg6d.mjs} +1 -1
  9. package/dist/cli/index.mjs +43 -28
  10. package/dist/client/react.d.mts +1 -1
  11. package/dist/client/react.mjs +1 -1
  12. package/dist/{cloud-C_Ok9rC5.mjs → cloud-HWFlbfLJ.mjs} +4 -4
  13. package/dist/{cloud-ask-ai-Cl-xaV8j.mjs → cloud-ask-ai-1k0q6OAH.mjs} +1 -1
  14. package/dist/{cloud-ask-ai-DcMe6xOf.d.mts → cloud-ask-ai-466g6uAi.d.mts} +1 -1
  15. package/dist/code-blocks-C9awWzEQ.mjs +871 -0
  16. package/dist/codeblocks-BzfkREEC.mjs +250 -0
  17. package/dist/{config-tiQCZ46q.mjs → config-CydaZ5PB.mjs} +52 -11
  18. package/dist/{dev-Tm9Ss4D6.mjs → dev-BA2kRuCn.mjs} +3 -3
  19. package/dist/docs-cloud-server.d.mts +2 -2
  20. package/dist/docs-cloud-server.mjs +2 -2
  21. package/dist/{doctor-O9o9kjN8.mjs → doctor-CQpQ2zZm.mjs} +177 -47
  22. package/dist/{downgrade-CYEaOctn.mjs → downgrade-BStNhyCc.mjs} +2 -2
  23. package/dist/i18n-CAlj1ADU.mjs +40 -0
  24. package/dist/index.d.mts +158 -3
  25. package/dist/index.mjs +7 -6
  26. package/dist/{init-Ch5On0zm.mjs → init-gnHQ_Pz9.mjs} +3 -3
  27. package/dist/{mcp-DuCPNHS-.mjs → mcp-DnxZZWpY.mjs} +10 -5
  28. package/dist/mcp.d.mts +122 -6
  29. package/dist/mcp.mjs +1943 -86
  30. package/dist/{package-version-C8PigBZk.mjs → package-version-DRIc08EU.mjs} +1 -1
  31. package/dist/{reading-time-CPAy1SWO.mjs → reading-time-BrTd3DIh.mjs} +277 -33
  32. package/dist/{review-lm3dt7yy.mjs → review-B6gyEFkD.mjs} +184 -14
  33. package/dist/{robots-DV7u4Ire.mjs → robots-4BUJxlvV.mjs} +4 -4
  34. package/dist/{robots-XVPs9lVz.mjs → robots-DAptQnkx.mjs} +1 -1
  35. package/dist/{search-BWuU70B0.mjs → search-CJIC1Cqo.mjs} +173 -79
  36. package/dist/{search-B6nEkx86.mjs → search-CbPm2x5g.mjs} +6 -4
  37. package/dist/{search-DFEhf9-s.d.mts → search-CfWvmVYA.d.mts} +3 -1
  38. package/dist/server.d.mts +108 -5
  39. package/dist/server.mjs +9 -8
  40. package/dist/{sitemap-BEiKy4Iy.mjs → sitemap-D6nP3J1Q.mjs} +8 -6
  41. package/dist/{sitemap-server-xEHtDUDB.mjs → sitemap-server-wsNLyVkb.mjs} +1 -1
  42. package/dist/{templates-uaauJcTO.mjs → templates-1Cod8KrJ.mjs} +11 -5
  43. package/dist/{types-XqGLsmOD.d.mts → types-BVgucdVm.d.mts} +194 -2
  44. package/dist/{upgrade-DzQtpgJM.mjs → upgrade-CXYRNV0C.mjs} +2 -2
  45. package/package.json +1 -1
  46. package/dist/agent-DXDOMJBw.mjs +0 -9
  47. package/dist/codeblocks-z9iT794h.mjs +0 -1713
  48. package/dist/review-B7goPYUb.mjs +0 -541
  49. /package/dist/{cloud-analytics-Dlk_byos.mjs → cloud-analytics-CSyFE6SS.mjs} +0 -0
  50. /package/dist/{errors-CW1LnxaQ.mjs → errors-BFUtdZfC.mjs} +0 -0
  51. /package/dist/{utils-D-xTRNuh.mjs → utils-DBCCkkJS.mjs} +0 -0
@@ -1,4 +1,4 @@
1
- import { i as detectPackageManagerFromProject, l as formatPackageManagerDetection, t as detectFramework, u as installCommand } from "./utils-D-xTRNuh.mjs";
1
+ import { i as detectPackageManagerFromProject, l as formatPackageManagerDetection, t as detectFramework, u as installCommand } from "./utils-DBCCkkJS.mjs";
2
2
  import pc from "picocolors";
3
3
  import * as p from "@clack/prompts";
4
4
 
@@ -1,3 +1,5 @@
1
+ import { Dt as DEFAULT_SITEMAP_XML_ROUTE, Gt as hasStructuredPageAgentContract, Kt as normalizePageAgentFrontmatter, S as DEFAULT_MCP_WELL_KNOWN_ROUTE, Tt as DEFAULT_SITEMAP_MD_ROUTE, c as DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE, f as DEFAULT_DOCS_CONFIG_ROUTE, h as DEFAULT_LLMS_FULL_TXT_ROUTE, i as DEFAULT_AGENT_FEEDBACK_ROUTE, m as DEFAULT_DOCS_DIAGNOSTICS_ROUTE, t as DEFAULT_AGENTS_MD_ROUTE, v as DEFAULT_LLMS_TXT_ROUTE, w as DEFAULT_SKILL_MD_ROUTE } from "./agent-DXkXi-pS.mjs";
2
+ import { n as DEFAULT_ROBOTS_TXT_ROUTE } from "./robots-DAptQnkx.mjs";
1
3
  import matter from "gray-matter";
2
4
 
3
5
  //#region src/define-docs.ts
@@ -158,41 +160,236 @@ function extendTheme(baseTheme, extensions) {
158
160
  }
159
161
 
160
162
  //#endregion
161
- //#region src/i18n.ts
162
- function normalizeSegment(value) {
163
- return value.replace(/^\/+|\/+$/g, "");
164
- }
165
- function splitSegments(value) {
166
- const cleaned = normalizeSegment(value);
167
- return cleaned ? cleaned.split("/").filter(Boolean) : [];
168
- }
169
- function resolveDocsI18n(config) {
170
- if (!config || !Array.isArray(config.locales)) return null;
171
- const locales = Array.from(new Set(config.locales.map((l) => l.trim()).filter(Boolean)));
172
- if (locales.length === 0) return null;
173
- return {
174
- locales,
175
- defaultLocale: config.defaultLocale && locales.includes(config.defaultLocale) ? config.defaultLocale : locales[0]
176
- };
163
+ //#region src/agent-conformance.ts
164
+ const DOCS_AGENT_CONTRACT_VERSION = "1.0";
165
+ function normalizeOrigin(value) {
166
+ return value.replace(/\/+$/, "");
177
167
  }
178
- function resolveDocsLocale(searchParams, i18n) {
179
- if (!i18n) return void 0;
180
- const raw = searchParams.get("lang") ?? searchParams.get("locale");
181
- if (!raw) return void 0;
182
- if (i18n.locales.includes(raw)) return raw;
183
- return i18n.defaultLocale;
184
- }
185
- function resolveDocsPath(pathname, entry) {
186
- const entryBase = normalizeSegment(entry || "docs") || "docs";
187
- const entryParts = splitSegments(entryBase);
188
- const pathParts = splitSegments(pathname);
189
- let rest = pathParts;
190
- if (entryParts.length > 0) {
191
- if (pathParts.slice(0, entryParts.length).join("/") === entryParts.join("/")) rest = pathParts.slice(entryParts.length);
168
+ function normalizeEntry(value) {
169
+ return value.replace(/^\/+|\/+$/g, "") || "docs";
170
+ }
171
+ /**
172
+ * The framework-neutral public surface every first-party adapter must implement.
173
+ * Adapter tests consume this list so new routes and invariants are added once.
174
+ */
175
+ function createDocsAgentContractCases(options = {}) {
176
+ const origin = normalizeOrigin(options.origin ?? "http://localhost");
177
+ const entry = normalizeEntry(options.entry ?? "docs");
178
+ const locale = options.locale === void 0 ? "fr" : options.locale;
179
+ const url = (route) => `${origin}${route.startsWith("/") ? route : `/${route}`}`;
180
+ const markdown = ["text/markdown"];
181
+ const json = ["application/json"];
182
+ const cases = [
183
+ {
184
+ surface: "discovery",
185
+ request: { url: url(DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE) },
186
+ expect: {
187
+ statuses: [200],
188
+ contentTypes: json
189
+ }
190
+ },
191
+ {
192
+ surface: "config",
193
+ request: { url: url(DEFAULT_DOCS_CONFIG_ROUTE) },
194
+ expect: {
195
+ statuses: [200],
196
+ contentTypes: json,
197
+ bodyIncludes: ["docs-config-map.v1"]
198
+ }
199
+ },
200
+ {
201
+ surface: "diagnostics",
202
+ request: { url: url(DEFAULT_DOCS_DIAGNOSTICS_ROUTE) },
203
+ expect: {
204
+ statuses: [200],
205
+ contentTypes: json,
206
+ bodyIncludes: ["docs-diagnostics.v1"]
207
+ }
208
+ },
209
+ {
210
+ surface: "feedback-schema",
211
+ request: { url: url(`${DEFAULT_AGENT_FEEDBACK_ROUTE}/schema`) },
212
+ expect: {
213
+ statuses: [200],
214
+ contentTypes: ["application/schema+json"]
215
+ }
216
+ },
217
+ {
218
+ surface: "markdown",
219
+ request: { url: url(`/${entry}.md`) },
220
+ expect: {
221
+ statuses: [200],
222
+ contentTypes: markdown,
223
+ bodyIncludes: ["Introduction"]
224
+ }
225
+ },
226
+ {
227
+ surface: "markdown-accept",
228
+ request: {
229
+ url: url(`/${entry}`),
230
+ init: { headers: { Accept: "text/markdown" } }
231
+ },
232
+ expect: {
233
+ statuses: [200],
234
+ contentTypes: markdown,
235
+ bodyIncludes: ["Introduction"]
236
+ }
237
+ },
238
+ {
239
+ surface: "markdown-missing",
240
+ request: { url: url(`/${entry}/missing.md`) },
241
+ expect: {
242
+ statuses: [200, 404],
243
+ contentTypes: markdown,
244
+ bodyIncludes: ["not found"]
245
+ }
246
+ },
247
+ {
248
+ surface: "llms",
249
+ request: { url: url(DEFAULT_LLMS_TXT_ROUTE) },
250
+ expect: {
251
+ statuses: [200],
252
+ contentTypes: ["text/plain"],
253
+ bodyIncludes: ["Introduction"]
254
+ }
255
+ },
256
+ {
257
+ surface: "llms-full",
258
+ request: { url: url(DEFAULT_LLMS_FULL_TXT_ROUTE) },
259
+ expect: {
260
+ statuses: [200],
261
+ contentTypes: ["text/plain"],
262
+ bodyIncludes: ["Introduction"]
263
+ }
264
+ },
265
+ {
266
+ surface: "agents",
267
+ request: { url: url(DEFAULT_AGENTS_MD_ROUTE) },
268
+ expect: {
269
+ statuses: [200],
270
+ contentTypes: markdown,
271
+ bodyIncludes: ["AGENTS.md"]
272
+ }
273
+ },
274
+ {
275
+ surface: "skill",
276
+ request: { url: url(DEFAULT_SKILL_MD_ROUTE) },
277
+ expect: {
278
+ statuses: [200],
279
+ contentTypes: markdown,
280
+ bodyIncludes: ["name:"]
281
+ }
282
+ },
283
+ {
284
+ surface: "sitemap-xml",
285
+ request: { url: url(DEFAULT_SITEMAP_XML_ROUTE) },
286
+ expect: {
287
+ statuses: [200],
288
+ contentTypes: ["application/xml", "text/xml"],
289
+ bodyIncludes: ["<urlset"]
290
+ }
291
+ },
292
+ {
293
+ surface: "sitemap-markdown",
294
+ request: { url: url(DEFAULT_SITEMAP_MD_ROUTE) },
295
+ expect: {
296
+ statuses: [200],
297
+ contentTypes: markdown,
298
+ bodyIncludes: ["Introduction"]
299
+ }
300
+ },
301
+ {
302
+ surface: "robots",
303
+ request: { url: url(DEFAULT_ROBOTS_TXT_ROUTE) },
304
+ expect: {
305
+ statuses: [200],
306
+ contentTypes: ["text/plain"],
307
+ bodyIncludes: ["User-agent:"]
308
+ }
309
+ },
310
+ {
311
+ surface: "mcp",
312
+ request: {
313
+ url: url(DEFAULT_MCP_WELL_KNOWN_ROUTE),
314
+ init: {
315
+ method: "POST",
316
+ headers: {
317
+ Accept: "application/json, text/event-stream",
318
+ "Content-Type": "application/json",
319
+ "MCP-Protocol-Version": "2025-11-25"
320
+ },
321
+ body: JSON.stringify({
322
+ jsonrpc: "2.0",
323
+ id: 1,
324
+ method: "initialize",
325
+ params: {
326
+ protocolVersion: "2025-11-25",
327
+ capabilities: {},
328
+ clientInfo: {
329
+ name: "agent-conformance",
330
+ version: DOCS_AGENT_CONTRACT_VERSION
331
+ }
332
+ }
333
+ })
334
+ }
335
+ },
336
+ expect: {
337
+ statuses: [200],
338
+ contentTypes: ["application/json", "text/event-stream"],
339
+ bodyIncludes: ["serverInfo", "protocolVersion"]
340
+ }
341
+ }
342
+ ];
343
+ if (locale) cases.splice(6, 0, {
344
+ surface: "markdown-locale",
345
+ request: { url: url(`/${entry}.md?lang=${encodeURIComponent(locale)}`) },
346
+ expect: {
347
+ statuses: [200],
348
+ contentTypes: markdown,
349
+ bodyIncludes: ["Bonjour"]
350
+ }
351
+ });
352
+ return cases;
353
+ }
354
+ function matchesContentType(actual, expected) {
355
+ const [mediaType] = actual.split(";", 1);
356
+ const normalized = mediaType?.trim().toLowerCase();
357
+ if (!normalized) return false;
358
+ return expected.some((value) => normalized === value.trim().toLowerCase());
359
+ }
360
+ async function runDocsAgentConformance(options) {
361
+ const cases = createDocsAgentContractCases(options);
362
+ const results = [];
363
+ for (const contractCase of cases) {
364
+ const issues = [];
365
+ try {
366
+ const response = await options.handle(new Request(contractCase.request.url, contractCase.request.init), contractCase.surface);
367
+ const contentType = response.headers.get("content-type") ?? "";
368
+ const body = await response.text();
369
+ if (!contractCase.expect.statuses.includes(response.status)) issues.push(`expected status ${contractCase.expect.statuses.join(" or ")}, received ${response.status}`);
370
+ if (!matchesContentType(contentType, contractCase.expect.contentTypes)) issues.push(`expected content-type ${contractCase.expect.contentTypes.join(" or ")}, received ${contentType || "<missing>"}`);
371
+ for (const requiredText of contractCase.expect.bodyIncludes ?? []) if (!body.toLowerCase().includes(requiredText.toLowerCase())) issues.push(`response body did not include ${JSON.stringify(requiredText)}`);
372
+ results.push({
373
+ surface: contractCase.surface,
374
+ passed: issues.length === 0,
375
+ status: response.status,
376
+ contentType,
377
+ issues
378
+ });
379
+ } catch (error) {
380
+ issues.push(error instanceof Error ? error.message : String(error));
381
+ results.push({
382
+ surface: contractCase.surface,
383
+ passed: false,
384
+ issues
385
+ });
386
+ }
192
387
  }
193
388
  return {
194
- slug: rest.join("/"),
195
- entryPath: entryBase
389
+ adapter: options.adapter,
390
+ contractVersion: DOCS_AGENT_CONTRACT_VERSION,
391
+ passed: results.every((result) => result.passed),
392
+ cases: results
196
393
  };
197
394
  }
198
395
 
@@ -327,6 +524,51 @@ function normalizeDateModified(value) {
327
524
  const date = new Date(value);
328
525
  return Number.isNaN(date.getTime()) ? value : date.toISOString();
329
526
  }
527
+ function buildAgentContractHowTo(value, fallback) {
528
+ const agent = normalizePageAgentFrontmatter(value);
529
+ if (!agent || !hasStructuredPageAgentContract(agent)) return void 0;
530
+ const steps = [];
531
+ for (const command of agent.commands ?? []) {
532
+ const run = typeof command === "string" ? command : command.run;
533
+ const description = typeof command === "string" ? void 0 : command.description;
534
+ const cwd = typeof command === "string" ? void 0 : command.cwd;
535
+ steps.push({
536
+ "@type": "HowToStep",
537
+ position: steps.length + 1,
538
+ name: description ?? "Run command",
539
+ text: cwd ? `${run} (from ${cwd})` : run
540
+ });
541
+ }
542
+ for (const verification of agent.verification ?? []) {
543
+ const description = typeof verification === "string" ? verification : verification.description ?? "Verify the result";
544
+ const details = typeof verification === "string" ? verification : [verification.run, verification.expect ? `Expected: ${verification.expect}` : void 0].filter(Boolean).join(". ");
545
+ steps.push({
546
+ "@type": "HowToStep",
547
+ position: steps.length + 1,
548
+ name: description,
549
+ text: details || description
550
+ });
551
+ }
552
+ const supplies = [...(agent.prerequisites ?? []).map((name) => ({
553
+ "@type": "HowToSupply",
554
+ name
555
+ })), ...(agent.files ?? []).map((name) => ({
556
+ "@type": "HowToSupply",
557
+ name
558
+ }))];
559
+ const about = Object.entries(agent.appliesTo ?? {}).flatMap(([kind, values]) => (typeof values === "string" ? [values] : values ?? []).map((name) => ({
560
+ "@type": "Thing",
561
+ name: `${kind}: ${name}`
562
+ })));
563
+ return {
564
+ "@type": "HowTo",
565
+ name: agent.task ?? fallback.title,
566
+ ...agent.outcome ?? fallback.description ? { description: agent.outcome ?? fallback.description } : {},
567
+ ...about.length > 0 ? { about } : {},
568
+ ...supplies.length > 0 ? { supply: supplies } : {},
569
+ ...steps.length > 0 ? { step: steps } : {}
570
+ };
571
+ }
330
572
  /**
331
573
  * Resolve the public docs site URL from existing agent-facing config.
332
574
  *
@@ -370,6 +612,8 @@ function buildDocsPageStructuredData(input) {
370
612
  }
371
613
  };
372
614
  if (input.description) result.description = input.description;
615
+ const agentContract = buildAgentContractHowTo(input.agent, input);
616
+ if (agentContract) result.mainEntity = agentContract;
373
617
  const dateModified = normalizeDateModified(input.dateModified);
374
618
  if (dateModified) result.dateModified = dateModified;
375
619
  return result;
@@ -437,4 +681,4 @@ function resolveReadingTimeFromSource(source, wordsPerMinute, options) {
437
681
  }
438
682
 
439
683
  //#endregion
440
- export { extendTheme as _, resolveReadingTimeOptions as a, defineDocs as b, buildPageTwitter as c, resolveOGImage as d, resolveTitle as f, createTheme as g, resolveDocsPath as h, resolveReadingTimeFromSource as i, renderDocsPageStructuredDataJson as l, resolveDocsLocale as m, resolvePageReadingTime as n, buildDocsPageStructuredData as o, resolveDocsI18n as p, resolveReadingTimeFromContent as r, buildPageOpenGraph as s, estimateReadingTimeMinutes as t, resolveDocsMetadataBaseUrl as u, deepMerge as v, resolveChangelogConfig as y };
684
+ export { extendTheme as _, resolveReadingTimeOptions as a, defineDocs as b, buildPageTwitter as c, resolveOGImage as d, resolveTitle as f, createTheme as g, runDocsAgentConformance as h, resolveReadingTimeFromSource as i, renderDocsPageStructuredDataJson as l, createDocsAgentContractCases as m, resolvePageReadingTime as n, buildDocsPageStructuredData as o, DOCS_AGENT_CONTRACT_VERSION as p, resolveReadingTimeFromContent as r, buildPageOpenGraph as s, estimateReadingTimeMinutes as t, resolveDocsMetadataBaseUrl as u, deepMerge as v, resolveChangelogConfig as y };
@@ -1,13 +1,19 @@
1
- import { a as ensureDocsReviewWorkflow, o as readDocsReviewConfigFromSource, s as resolveDocsReviewConfig } from "./review-B7goPYUb.mjs";
2
- import { f as readTopLevelStringProperty, i as loadDocsConfigModule, m as resolveDocsContentDir, p as resolveDocsConfigPath } from "./config-tiQCZ46q.mjs";
1
+ import { Bt as PAGE_AGENT_CONTRACT_FIELDS, Gt as hasStructuredPageAgentContract, Kt as normalizePageAgentFrontmatter, M as buildDocsAgentDiscoverySpec, O as DOCS_CONFIG_MAP_TOP_LEVEL_KEYS, P as buildDocsConfigMap, Wt as getPageAgentFrontmatterIssues, f as DEFAULT_DOCS_CONFIG_ROUTE, u as DEFAULT_DOCS_API_ROUTE } from "./agent-DXkXi-pS.mjs";
2
+ import "./search-CJIC1Cqo.mjs";
3
+ import { c as resolveDocsReviewConfig, o as ensureDocsReviewWorkflow, s as readDocsReviewConfigFromSource, t as runDocsGoldenTasks } from "./agent-evals-BD17jOqL.mjs";
4
+ import { createFilesystemDocsMcpSource, getDocsConfigSchema, resolveDocsMcpConfig } from "./mcp.mjs";
5
+ import "./code-blocks-C9awWzEQ.mjs";
6
+ import { a as loadDocsConfigModuleResultWithProjectEnv, h as resolveDocsContentDir, m as resolveDocsConfigPath, p as readTopLevelStringProperty } from "./config-CydaZ5PB.mjs";
7
+ import { t as detectFramework } from "./utils-DBCCkkJS.mjs";
8
+ import { n as analyzeAgentUsefulness, r as createAgentUsefulnessPagesFromMcp, t as analyzeAgentSurfaceDrift } from "./agent-surface-drift-LS8zQgbq.mjs";
3
9
  import matter from "gray-matter";
4
10
  import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
5
11
  import path from "node:path";
6
- import pc from "picocolors";
7
12
  import { execFileSync } from "node:child_process";
13
+ import pc from "picocolors";
8
14
 
9
15
  //#region src/cli/review.ts
10
- const DOCS_FILE_PATTERN = /\.(?:md|mdx)$/;
16
+ const DOCS_FILE_PATTERN = /\.(?:md|mdx|svx)$/;
11
17
  const IGNORED_DIRS = new Set([
12
18
  ".git",
13
19
  ".next",
@@ -19,6 +25,26 @@ const IGNORED_DIRS = new Set([
19
25
  "node_modules",
20
26
  "out"
21
27
  ]);
28
+ function isPlainRecord(value) {
29
+ return typeof value === "object" && value !== null && !Array.isArray(value);
30
+ }
31
+ function resolveGoldenEvaluationTasks(evaluationInput) {
32
+ if (evaluationInput === void 0 || typeof evaluationInput === "boolean") return void 0;
33
+ if (!isPlainRecord(evaluationInput)) return { evaluationConfig: evaluationInput };
34
+ if (evaluationInput.enabled === false) return void 0;
35
+ if ("enabled" in evaluationInput && typeof evaluationInput.enabled !== "boolean") return evaluationInput;
36
+ if (!("tasks" in evaluationInput)) return void 0;
37
+ const runtimeTasks = evaluationInput.tasks;
38
+ if (!Array.isArray(runtimeTasks)) return runtimeTasks;
39
+ return runtimeTasks.map((task) => {
40
+ if (!isPlainRecord(task)) return task;
41
+ return {
42
+ ...task,
43
+ tokenBudget: task.tokenBudget ?? evaluationInput.tokenBudget,
44
+ topK: task.topK ?? evaluationInput.topK
45
+ };
46
+ });
47
+ }
22
48
  function parseReviewArgs(argv) {
23
49
  const parsed = {};
24
50
  for (let index = 0; index < argv.length; index += 1) {
@@ -84,7 +110,8 @@ async function runReview(options = {}) {
84
110
  const rootDir = process.cwd();
85
111
  const configPath = resolveDocsConfigPath(rootDir, options.configPath);
86
112
  const configContent = readFileSync(configPath, "utf-8");
87
- const config = (await loadDocsConfigModule(rootDir, options.configPath))?.config;
113
+ const configLoad = await loadDocsConfigModuleResultWithProjectEnv(rootDir, options.configPath);
114
+ const config = configLoad.status === "evaluated" ? configLoad.config : void 0;
88
115
  const review = withReviewOptionOverrides(resolveDocsReviewConfig(config?.review ?? readDocsReviewConfigFromSource(configContent)), options.mode, options.scoreThreshold);
89
116
  if (options.setup) {
90
117
  const result = ensureDocsReviewWorkflow({
@@ -100,7 +127,8 @@ async function runReview(options = {}) {
100
127
  }
101
128
  if (!review.enabled) {
102
129
  const report = {
103
- score: 100,
130
+ status: "disabled",
131
+ score: null,
104
132
  threshold: review.score.threshold,
105
133
  mode: options.ci ? review.ci.mode : "local",
106
134
  reviewedFiles: [],
@@ -122,6 +150,18 @@ async function runReview(options = {}) {
122
150
  rootDir,
123
151
  contentDir
124
152
  });
153
+ const mcpSource = createFilesystemDocsMcpSource({
154
+ rootDir,
155
+ entry,
156
+ contentDir,
157
+ siteTitle: typeof config?.nav?.title === "string" ? config.nav.title : "Documentation"
158
+ });
159
+ const corpusPages = await Promise.resolve(mcpSource.getPages());
160
+ const usefulness = analyzeAgentUsefulness({
161
+ rootDir,
162
+ pages: createAgentUsefulnessPagesFromMcp(rootDir, corpusPages),
163
+ projectFramework: detectFramework(rootDir) ?? void 0
164
+ });
125
165
  const findings = collectReviewFindings({
126
166
  rootDir,
127
167
  entry,
@@ -129,15 +169,95 @@ async function runReview(options = {}) {
129
169
  files: relevantFiles,
130
170
  review
131
171
  });
172
+ const relevantSet = new Set(relevantFiles);
173
+ for (const issue of usefulness.findings) {
174
+ if (!relevantSet.has(issue.file)) continue;
175
+ pushFinding(findings, review, {
176
+ rule: issue.category === "command" ? "commandHealth" : issue.category === "related" ? "relatedCoverage" : "agentContext",
177
+ code: issue.code,
178
+ severity: issue.severity === "warning" ? "warn" : issue.severity === "error" ? "error" : "suggestion",
179
+ file: issue.file,
180
+ line: issue.line,
181
+ message: issue.message
182
+ });
183
+ }
184
+ const evaluationInput = config?.agent?.evaluations;
185
+ const evaluations = await runDocsGoldenTasks(corpusPages, resolveGoldenEvaluationTasks(evaluationInput));
186
+ if (relevantFiles.length > 0 && evaluations.status === "unmeasured") pushFinding(findings, review, {
187
+ rule: "goldenTasks",
188
+ code: "golden-tasks-unmeasured",
189
+ severity: "warn",
190
+ file: toPosixPath(path.relative(rootDir, configPath)),
191
+ message: evaluationInput === false || typeof evaluationInput === "object" && evaluationInput.enabled === false ? "Golden agent tasks are disabled, so retrieval usefulness is unmeasured." : "No golden agent tasks are configured; retrieval, citations, version selection, executable examples, and token usage are unmeasured."
192
+ });
193
+ for (const task of evaluations.tasks.filter((task) => !task.passed)) pushFinding(findings, review, {
194
+ rule: "goldenTasks",
195
+ code: "golden-task-failed",
196
+ severity: "warn",
197
+ file: toPosixPath(path.relative(rootDir, configPath)),
198
+ message: `Golden task ${JSON.stringify(task.id)} scored ${task.score}/100: ${task.issues.join(" ")}`
199
+ });
200
+ if (configLoad.status === "static-fallback") pushFinding(findings, review, {
201
+ rule: "configConfidence",
202
+ code: "config-static-fallback",
203
+ severity: "warn",
204
+ file: toPosixPath(path.relative(rootDir, configPath)),
205
+ message: `docs.config could not be evaluated; review used partial static parsing. ${configLoad.error}`
206
+ });
207
+ else {
208
+ const evaluatedConfig = configLoad.config;
209
+ const searchEnabled = evaluatedConfig.search !== false && !(typeof evaluatedConfig.search === "object" && evaluatedConfig.search?.enabled === false);
210
+ const mcp = resolveDocsMcpConfig(evaluatedConfig.mcp, { defaultName: typeof evaluatedConfig.nav?.title === "string" ? evaluatedConfig.nav.title : void 0 });
211
+ const discovery = buildDocsAgentDiscoverySpec({
212
+ origin: "http://localhost",
213
+ entry,
214
+ search: evaluatedConfig.search,
215
+ mcp
216
+ });
217
+ const configuredAgentReviewPaths = Object.values(buildDocsConfigMap(evaluatedConfig).pointers).map((pointer) => pointer.path).filter((optionPath) => optionPath === "agent" || optionPath.startsWith("agent.") || optionPath === "review" || optionPath.startsWith("review."));
218
+ const drift = analyzeAgentSurfaceDrift({
219
+ configOptionPaths: [...new Set([...DOCS_CONFIG_MAP_TOP_LEVEL_KEYS, ...configuredAgentReviewPaths])],
220
+ schemaOptions: getDocsConfigSchema().options,
221
+ agentContractFields: PAGE_AGENT_CONTRACT_FIELDS,
222
+ discovery,
223
+ expected: {
224
+ entry,
225
+ search: {
226
+ enabled: searchEnabled,
227
+ endpoint: `${DEFAULT_DOCS_API_ROUTE}?query={query}`
228
+ },
229
+ mcp: {
230
+ enabled: mcp.enabled,
231
+ endpoint: mcp.route,
232
+ tools: mcp.tools
233
+ },
234
+ routes: {
235
+ "api.docs": DEFAULT_DOCS_API_ROUTE,
236
+ "api.config": DEFAULT_DOCS_CONFIG_ROUTE,
237
+ "config.endpoint": DEFAULT_DOCS_CONFIG_ROUTE
238
+ }
239
+ }
240
+ });
241
+ for (const issue of drift) pushFinding(findings, review, {
242
+ rule: "agentSurfaceDrift",
243
+ code: issue.code,
244
+ severity: "error",
245
+ file: toPosixPath(path.relative(rootDir, configPath)),
246
+ message: issue.message
247
+ });
248
+ }
132
249
  const score = calculateReviewScore(findings, review);
133
250
  const mode = options.ci ? review.ci.mode : "local";
134
251
  const report = {
252
+ status: "measured",
135
253
  score,
136
254
  threshold: review.score.threshold,
137
255
  mode,
138
256
  reviewedFiles: relevantFiles,
139
257
  changedFiles,
140
- findings
258
+ findings,
259
+ usefulness: usefulness.metrics,
260
+ evaluations
141
261
  };
142
262
  if (options.json) console.log(JSON.stringify(report, null, 2));
143
263
  else printReviewReport(report);
@@ -214,7 +334,8 @@ function collectReviewFindings(options) {
214
334
  checkAgentContext(findings, options.review, {
215
335
  file,
216
336
  source,
217
- rootDir: options.rootDir
337
+ rootDir: options.rootDir,
338
+ agent: parsed.data.agent
218
339
  });
219
340
  }
220
341
  checkBrokenLinks(findings, options.review, {
@@ -294,6 +415,39 @@ function checkCodeFences(findings, review, options) {
294
415
  }
295
416
  }
296
417
  function checkAgentContext(findings, review, options) {
418
+ for (const issue of getPageAgentFrontmatterIssues(options.agent)) pushFinding(findings, review, {
419
+ rule: "agentContext",
420
+ severity: "warn",
421
+ file: options.file,
422
+ line: 1,
423
+ message: `Invalid ${issue.path}: ${issue.message}${/[.!?]$/.test(issue.message) ? "" : "."}`
424
+ });
425
+ const agent = normalizePageAgentFrontmatter(options.agent);
426
+ if (hasStructuredPageAgentContract(agent)) {
427
+ const missing = [!agent?.task ? "task" : void 0, !agent?.outcome ? "outcome" : void 0].filter(Boolean).join(" and ");
428
+ if (missing) pushFinding(findings, review, {
429
+ rule: "agentContext",
430
+ severity: "suggestion",
431
+ file: options.file,
432
+ line: 1,
433
+ message: `Structured agent contract is missing ${missing}.`
434
+ });
435
+ if (agent?.commands?.length && !agent.verification?.length) pushFinding(findings, review, {
436
+ rule: "agentContext",
437
+ severity: "suggestion",
438
+ file: options.file,
439
+ line: 1,
440
+ message: "Structured agent contract defines commands without verification steps."
441
+ });
442
+ if (agent?.sideEffects?.length && !agent.rollback?.length) pushFinding(findings, review, {
443
+ rule: "agentContext",
444
+ severity: "suggestion",
445
+ file: options.file,
446
+ line: 1,
447
+ message: "Structured agent contract defines side effects without rollback guidance."
448
+ });
449
+ if (agent?.task && agent.outcome) return;
450
+ }
297
451
  if (options.source.includes("<Agent>") || options.source.includes("</Agent>")) return;
298
452
  if (existsSync(path.join(path.dirname(path.join(options.rootDir, options.file)), "agent.md"))) return;
299
453
  if (!/\b(install|configure|setup|implement|defineDocs|docs\.config|MCP|agent)\b/i.test(options.source)) return;
@@ -332,11 +486,17 @@ function printReviewReport(report) {
332
486
  console.log(`Changed files: ${report.changedFiles.length}`);
333
487
  console.log(`Reviewed docs files: ${report.reviewedFiles.length}`);
334
488
  console.log(`Findings: ${counts.error} error${counts.error === 1 ? "" : "s"}, ${counts.warn} warning${counts.warn === 1 ? "" : "s"}, ${counts.suggestion} suggestion${counts.suggestion === 1 ? "" : "s"}`);
335
- if (report.reviewedFiles.length === 0) {
489
+ if (report.usefulness) console.log(`Useful Agent blocks: ${report.usefulness.agentBlocks.useful}/${report.usefulness.agentBlocks.total}; task-complete pages: ${report.usefulness.taskCompleteness.completePages}/${report.usefulness.actionablePages}`);
490
+ if (report.evaluations) console.log(`Golden tasks: ${report.evaluations.status === "unmeasured" ? "unmeasured" : `${report.evaluations.passedTaskCount}/${report.evaluations.taskCount} passed (${report.evaluations.score}/100)`}`);
491
+ if (report.reviewedFiles.length === 0 && report.findings.length === 0) {
336
492
  console.log("");
337
493
  console.log(pc.green("No docs changes detected. Skipping review."));
338
494
  return;
339
495
  }
496
+ if (report.reviewedFiles.length === 0) {
497
+ console.log("");
498
+ console.log(pc.yellow("No docs files changed; reporting project-wide findings."));
499
+ }
340
500
  if (report.findings.length === 0) {
341
501
  console.log("");
342
502
  console.log(pc.green("No docs review findings."));
@@ -376,7 +536,17 @@ function selectReviewFiles(options) {
376
536
  const pageFiles = new Set(options.pages.map((page) => page.relativePath));
377
537
  const normalizedConfigPath = toPosixPath(options.configPath);
378
538
  if (options.changedFiles.includes(normalizedConfigPath)) return Array.from(pageFiles).sort();
379
- return options.changedFiles.map(toPosixPath).filter((file) => pageFiles.has(file)).sort();
539
+ const selected = /* @__PURE__ */ new Set();
540
+ for (const changedFile of options.changedFiles.map(toPosixPath)) {
541
+ if (pageFiles.has(changedFile)) {
542
+ selected.add(changedFile);
543
+ continue;
544
+ }
545
+ if (!changedFile.endsWith("/agent.md") && changedFile !== "agent.md") continue;
546
+ const sibling = options.pages.find((page) => path.posix.dirname(page.relativePath) === path.posix.dirname(changedFile));
547
+ if (sibling) selected.add(sibling.relativePath);
548
+ }
549
+ return Array.from(selected).sort();
380
550
  }
381
551
  function getChangedFiles(rootDir, options) {
382
552
  const ranges = [
@@ -404,7 +574,7 @@ function getChangedFiles(rootDir, options) {
404
574
  function scanDocsPages(rootDir, contentDir, entry) {
405
575
  const contentRoot = path.isAbsolute(contentDir) ? contentDir : path.join(rootDir, contentDir);
406
576
  if (!existsSync(contentRoot)) return [];
407
- return listFiles(contentRoot).filter((file) => DOCS_FILE_PATTERN.test(file)).map((absolutePath) => {
577
+ return listFiles(contentRoot).filter((file) => DOCS_FILE_PATTERN.test(file) && path.basename(file) !== "agent.md").map((absolutePath) => {
408
578
  const relativeToContent = toPosixPath(path.relative(contentRoot, absolutePath));
409
579
  const relativePath = toPosixPath(path.relative(rootDir, absolutePath));
410
580
  const slug = docsSlugFromFile(relativeToContent);
@@ -434,9 +604,9 @@ function listFiles(dir) {
434
604
  return files;
435
605
  }
436
606
  function docsSlugFromFile(relativePath) {
437
- const withoutExt = relativePath.replace(/\.(?:md|mdx)$/, "");
438
- if (withoutExt === "page" || withoutExt === "index") return "";
439
- if (withoutExt.endsWith("/page") || withoutExt.endsWith("/index")) return withoutExt.replace(/\/(?:page|index)$/, "");
607
+ const withoutExt = relativePath.replace(/\.(?:md|mdx|svx)$/, "");
608
+ if (withoutExt === "page" || withoutExt === "+page" || withoutExt === "index") return "";
609
+ if (withoutExt.endsWith("/page") || withoutExt.endsWith("/+page") || withoutExt.endsWith("/index")) return withoutExt.replace(/\/(?:\+?page|index)$/, "");
440
610
  return withoutExt;
441
611
  }
442
612
  function normalizeInternalHref(href) {
@@ -1,7 +1,7 @@
1
- import "./agent-CaOBIVFy.mjs";
2
- 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-XVPs9lVz.mjs";
3
- import { f as readTopLevelStringProperty, i as loadDocsConfigModule, o as readBooleanProperty, p as resolveDocsConfigPath, t as extractNestedObjectLiteral, u as readStringProperty } from "./config-tiQCZ46q.mjs";
4
- import { t as detectFramework } from "./utils-D-xTRNuh.mjs";
1
+ import "./agent-DXkXi-pS.mjs";
2
+ 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-DAptQnkx.mjs";
3
+ import { d as readStringProperty, i as loadDocsConfigModule, m as resolveDocsConfigPath, p as readTopLevelStringProperty, s as readBooleanProperty, t as extractNestedObjectLiteral } from "./config-CydaZ5PB.mjs";
4
+ import { t as detectFramework } from "./utils-DBCCkkJS.mjs";
5
5
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
6
  import path from "node:path";
7
7
  import pc from "picocolors";
@@ -1,4 +1,4 @@
1
- import { S as DEFAULT_MCP_WELL_KNOWN_ROUTE, T as DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE, X as normalizeDocsPathSegment, a as DEFAULT_AGENT_MD_ROUTE, b as DEFAULT_MCP_PUBLIC_ROUTE, 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, kt as resolveDocsSitemapConfig, l as DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE, n as DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE, o as DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE, t as DEFAULT_AGENTS_MD_ROUTE, v as DEFAULT_LLMS_TXT_ROUTE, w as DEFAULT_SKILL_MD_ROUTE, y as DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE } from "./agent-CaOBIVFy.mjs";
1
+ import { $ as normalizeDocsPathSegment, Nt as resolveDocsSitemapConfig, S as DEFAULT_MCP_WELL_KNOWN_ROUTE, T as DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE, a as DEFAULT_AGENT_MD_ROUTE, b as DEFAULT_MCP_PUBLIC_ROUTE, 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, l as DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE, n as DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE, o as DEFAULT_AGENT_MD_WELL_KNOWN_ROUTE, t as DEFAULT_AGENTS_MD_ROUTE, v as DEFAULT_LLMS_TXT_ROUTE, w as DEFAULT_SKILL_MD_ROUTE, y as DEFAULT_LLMS_TXT_WELL_KNOWN_ROUTE } from "./agent-DXkXi-pS.mjs";
2
2
 
3
3
  //#region src/robots.ts
4
4
  const DEFAULT_ROBOTS_TXT_ROUTE = "/robots.txt";