@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.
- package/dist/agent-BXvi0uhS.mjs +623 -0
- package/dist/{agent-CaOBIVFy.mjs → agent-DXkXi-pS.mjs} +765 -22
- package/dist/agent-evals-BD17jOqL.mjs +1166 -0
- package/dist/agent-export-n-a0GbeU.mjs +854 -0
- package/dist/agent-scope-B8lptqCd.mjs +283 -0
- package/dist/agent-surface-drift-LS8zQgbq.mjs +1429 -0
- package/dist/{agents-DjhjZaNP.mjs → agents-BJaRQimo.mjs} +7 -5
- package/dist/{analytics-BZEwTK-8.mjs → analytics-Bx44lg6d.mjs} +1 -1
- package/dist/cli/index.mjs +43 -28
- package/dist/client/react.d.mts +1 -1
- package/dist/client/react.mjs +1 -1
- package/dist/{cloud-C_Ok9rC5.mjs → cloud-HWFlbfLJ.mjs} +4 -4
- package/dist/{cloud-ask-ai-Cl-xaV8j.mjs → cloud-ask-ai-1k0q6OAH.mjs} +1 -1
- package/dist/{cloud-ask-ai-DcMe6xOf.d.mts → cloud-ask-ai-466g6uAi.d.mts} +1 -1
- package/dist/code-blocks-C9awWzEQ.mjs +871 -0
- package/dist/codeblocks-BzfkREEC.mjs +250 -0
- package/dist/{config-tiQCZ46q.mjs → config-CydaZ5PB.mjs} +52 -11
- package/dist/{dev-Tm9Ss4D6.mjs → dev-BA2kRuCn.mjs} +3 -3
- package/dist/docs-cloud-server.d.mts +2 -2
- package/dist/docs-cloud-server.mjs +2 -2
- package/dist/{doctor-O9o9kjN8.mjs → doctor-CQpQ2zZm.mjs} +177 -47
- package/dist/{downgrade-CYEaOctn.mjs → downgrade-BStNhyCc.mjs} +2 -2
- package/dist/i18n-CAlj1ADU.mjs +40 -0
- package/dist/index.d.mts +158 -3
- package/dist/index.mjs +7 -6
- package/dist/{init-Ch5On0zm.mjs → init-gnHQ_Pz9.mjs} +3 -3
- package/dist/{mcp-DuCPNHS-.mjs → mcp-DnxZZWpY.mjs} +10 -5
- package/dist/mcp.d.mts +122 -6
- package/dist/mcp.mjs +1943 -86
- package/dist/{package-version-C8PigBZk.mjs → package-version-DRIc08EU.mjs} +1 -1
- package/dist/{reading-time-CPAy1SWO.mjs → reading-time-BrTd3DIh.mjs} +277 -33
- package/dist/{review-lm3dt7yy.mjs → review-B6gyEFkD.mjs} +184 -14
- package/dist/{robots-DV7u4Ire.mjs → robots-4BUJxlvV.mjs} +4 -4
- package/dist/{robots-XVPs9lVz.mjs → robots-DAptQnkx.mjs} +1 -1
- package/dist/{search-BWuU70B0.mjs → search-CJIC1Cqo.mjs} +173 -79
- package/dist/{search-B6nEkx86.mjs → search-CbPm2x5g.mjs} +6 -4
- package/dist/{search-DFEhf9-s.d.mts → search-CfWvmVYA.d.mts} +3 -1
- package/dist/server.d.mts +108 -5
- package/dist/server.mjs +9 -8
- package/dist/{sitemap-BEiKy4Iy.mjs → sitemap-D6nP3J1Q.mjs} +8 -6
- package/dist/{sitemap-server-xEHtDUDB.mjs → sitemap-server-wsNLyVkb.mjs} +1 -1
- package/dist/{templates-uaauJcTO.mjs → templates-1Cod8KrJ.mjs} +11 -5
- package/dist/{types-XqGLsmOD.d.mts → types-BVgucdVm.d.mts} +194 -2
- package/dist/{upgrade-DzQtpgJM.mjs → upgrade-CXYRNV0C.mjs} +2 -2
- package/package.json +1 -1
- package/dist/agent-DXDOMJBw.mjs +0 -9
- package/dist/codeblocks-z9iT794h.mjs +0 -1713
- package/dist/review-B7goPYUb.mjs +0 -541
- /package/dist/{cloud-analytics-Dlk_byos.mjs → cloud-analytics-CSyFE6SS.mjs} +0 -0
- /package/dist/{errors-CW1LnxaQ.mjs → errors-BFUtdZfC.mjs} +0 -0
- /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-
|
|
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/
|
|
162
|
-
|
|
163
|
-
|
|
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
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
function
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
195
|
-
|
|
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,
|
|
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 {
|
|
2
|
-
import
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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(/\/(
|
|
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-
|
|
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-
|
|
3
|
-
import {
|
|
4
|
-
import { t as detectFramework } from "./utils-
|
|
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 {
|
|
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";
|